10

Thing状態プロパティをA | B | C持つ型が
あり、有効な状態遷移がであるとしA->B, A->C, C->Aます。

私は書くことができます:

transitionToA :: Thing -> Maybe Thing

に遷移できない状態にあったNothing場合に返されます。ThingA

しかし、適切な型でのみ遷移を呼び出すことができるように、型と遷移関数を定義したいと思います。

別のタイプを作成するオプションがありますがAThing BThing CThing、複雑なケースでは維持できないようです。

別のアプローチは、各状態を独自の型としてエンコードすることです。

data A = A Thing
data B = B Thing
data C = C Thing

transitionCToA :: C Thing -> A Thing

これは私にはきれいに思えます。しかし、A、B、C は、遷移関数を除くすべての関数をマップできるファンクターであることがわかりました。

型クラスを使用すると、次のようなものを作成できます。

class ToA t where  
    toA :: t -> A Thing

これはまだきれいに見えます。

Haskell と PureScript で機能する他の優先アプローチはありますか?

4

4 に答える 4

10

以下は、(ファントムの可能性がある) 型パラメーターを使用して a がどの状態にあるかを追跡するかなり単純な方法ですThing

{-# LANGUAGE DataKinds, KindSignatures #-}
-- note: not exporting the constructors of Thing
module Thing (Thing, transAB, transAC, transCA) where

data State = A | B | C
data Thing (s :: State) = {- elided; can even be a data family instead -}

transAB :: Thing A -> Thing B
transAC :: Thing A -> Thing C
transCA :: Thing C -> Thing A

transAB = {- elided -}
transAC = {- elided -}
transCA = {- elided -}
于 2015-08-17T21:44:08.727 に答える
5

John が提案したように、型クラス (PureScript で利用可能) をファントム型と共に使用できますが、型クラスをパスの型の最終エンコーディングとして使用します。

data A -- States at the type level
data B
data C

class Path p where
  ab :: p A B -- One-step paths
  ac :: p A C
  ca :: p C A
  trans :: forall a b c. p c b -> p b a -> p c a -- Joining paths
  refl :: forall a. p a a

これで、有効なパスのタイプを作成できます。

type ValidPath a b = forall p. (Path p) => p a b

roundTrip :: ValidPath A A
roundTrip = trans ca ac

パスは、指定したワンステップ パスを使用してのみ作成できます。

パスを使用するようにインスタンスを作成できますが、重要なことに、どのインスタンスもタイプ レベルで有効な遷移を尊重する必要があります。

たとえば、パスの長さを計算する解釈は次のとおりです。

newtype Length = Length Int

instance pathLength :: Path Length where
  ab = Length 1
  ac = Length 1
  ca = Length 1
  trans (Length n) (Length m) = Length (n + m)
  refl = Length 0
于 2015-08-18T17:56:06.173 に答える
2

あなたの目標は、開発者が不正な遷移を実行するのを防ぐことであるため、ファントム タイプを調べることをお勧めします。ファントム型を使用すると、型システムのより高度な機能を利用することなく、型安全な遷移をモデル化できます。そのため、多くの言語に移植できます。

上記の問題の PureScript エンコーディングは次のとおりです。

foreign import data A :: *
foreign import data B :: *
foreign import data C :: *

data Thing a = Thing

transitionToA :: Thing C -> Thing A

ファントム型は、2 つの異なる状態が同じ状態に遷移できないというプロパティがある場合に有効な状態遷移をモデル化するのにうまく機能します (すべての状態がその状態に遷移できる場合を除く)。型クラス ( class CanTransitionToA a where trans :: Thing a -> Thing A) を使用することでこの制限を回避できますが、この時点で、他のアプローチを調査する必要があります。

于 2015-08-18T14:18:33.653 に答える
2

後で処理できるようにトランジションのリストを保存したい場合は、次のようにすることができます。

{-# LANGUAGE DataKinds, GADTs, KindSignatures, PolyKinds #-}

data State = A | B | C
data Edge (a :: State) (b :: State) where
    EdgeAB :: Edge A B
    EdgeAC :: Edge A C
    EdgeCA :: Edge C A

data Domino (f :: k -> k -> *) (a :: k) (b :: k)  where
    I :: Domino f a a
    (:>>:) :: f a b -> Domino f b c -> Domino f a c

infixr :>>:

example :: Domino Edge A B
example = EdgeAC :>>: EdgeCA :>>: EdgeAB :>>: I

Pathの連結関数を書くことで、それを のインスタンスに変えることができますDomino:

{-# LANGUAGE FlexibleInstances #-}
instance Path (Domino Edge) where
    ab = EdgeAB :>>: I
    ac = EdgeAC :>>: I
    ca = EdgeCA :>>: I

    refl = I
    trans I es' = es'
    trans (e :>>: es) es' = e :>>: (es `trans` es')

実際、これは、Hackage が「インデックス付きモノイド」を定義するパッケージを既に持っているかどうか疑問に思います。

class IMonoid (m :: k -> k -> *) where
    imempty :: m a a
    imappend :: m a b -> m b c -> m a c

instance IMonoid (Domino e) where
    imempty = I
    imappend I es' = es'
    imappend (e :>>: es) es' = e :>>: (es `imappend` es')
于 2015-08-19T01:30:27.990 に答える