Haskell中的递归类型同义词 - '循环类型同义词声明'

时间:2016-05-24 17:06:51

标签: haskell types

我目前正在阅读CAR Hoare的CSP Book,并尝试使用Haskell实现其早期示例。

本书首先定义了一个过程代数 - 本质上,过程接受事件并返回另一个过程或符号(bleep),表明该过程不参与此事件。 STOP进程是针对任何事件返回bleep的进程。

这本书建议你在LISP中这样做,所以我使用了Racket。 STOP的定义非常简单:

(define (STOP event) 'bleep)

这是一个'自动售货机'流程,只接受事件coin并返回STOP

(define (coin-to-stop event)
  (case event
    ['coin STOP]
    [else 'bleep]))

然后我尝试在Haskell中实现这些相同的概念。我们没有像Racket那样的符号,所以让我们定义Event

data Event = Ev String deriving (Eq)

与Racket不同,我们不能仅仅捏造'bleep和流程之间的差异,因此我们将使用Maybe来定义流程的类型:

type Process = Event -> Maybe Process

Just p对应某个流程,Nothing对应'bleep

现在我们可以定义STOP进程:

stop :: Process
stop _ = Nothing

和'自动售货机'流程:

coinToStop :: Process
coinToStop (Ev "coin") = Just stop
coinToStop _           = Nothing

不幸的是,这不能编译,因为你不能有循环类型定义:

Cycle in type synonym declarations:
  src\Csp.hs: type Process = Event -> Maybe Process

我和newtype玩了一会儿,但我真的不明白我在那里做什么。

我意识到我可以使用以下方法实现大致相同的解决方案:

data ProcResult = P (Maybe Process)
type Process = Event -> ProcResult

但这似乎不必要地丑陋。

在Haskell中表示Process概念的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

Haskell有三种类型定义:

  1. data:定义一个全面的"数据类型。
  2. newtype:定义"同构" type,类似于 opaque类型的同义词
  3. type:定义透明类型的同义词。就像C / C ++中的typedef一样。
  4. 因此您正在使用type并尝试定义递归类型的同义词:

    type Process = Event -> Maybe Process
    

    嗯,Haskell中的一条规则是,在任何情况下,如果你有一个type同义词,你可以用它的扩展替换它,程序的工作原理相同。 Haskell将扩展type个同义词,以便弄清楚它们代表什么类型。所以当你写这个:

    stop :: Process
    

    你要求Haskell以这种方式扩展它:

    stop :: Event -> Maybe Process
    stop :: Event -> Maybe (Event -> Maybe Process)
    stop :: Event -> Maybe (Event -> Maybe (Event -> Maybe Process))
         .
         .
         .
    

    总而言之,Haskell中的type个同义词根本不允许递归。允许newtypedata递归,这就是为什么你应该在这里使用newtype的原因。

答案 1 :(得分:1)

感谢上午,我找到了一个解决方案:

data Event = Ev String deriving (Eq)
newtype Process = Process { run :: Event -> Maybe Process }
                  deriving (Show)

stop :: Process
stop = Process (const Nothing)

coin :: Event
coin = Ev "coin"

coinAccepter :: Process
coinAccepter = Process (\x -> case x of 
                                (Ev "coin") -> Just stop
                                _           -> Nothing)

prefix :: Event -> Process -> Process
prefix c p = Process (\x -> if x == c then Just p else Nothing)

现在是时候去学习如何将runbind联系起来......