我目前正在阅读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
概念的正确方法是什么?
答案 0 :(得分:3)
Haskell有三种类型定义:
data
:定义一个全面的"数据类型。newtype
:定义"同构" type,类似于 opaque类型的同义词。type
:定义透明类型的同义词。就像C / C ++中的typedef
一样。因此您正在使用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
个同义词根本不允许递归。允许newtype
和data
递归,这就是为什么你应该在这里使用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)
现在是时候去学习如何将run
与bind
联系起来......