【问题标题】:State Machine Pattern in Haskell : infinite type errorHaskell 中的状态机模式:无限类型错误
【发布时间】:2011-02-12 21:44:16
【问题描述】:

我试图在 Haskell 中实现一个状态机。简化版如下:

在任何状态下,你都可以给机器一个整数,然后你得到一个整数。在状态 A 中,机器将其输入加倍。在状态 B 中,机器只是将您的输入返回给您。每当您在任一状态中看到零时,请更改为另一种状态。否则,状态不会改变。

这是我的方法:让每个状态都是一个函数,该函数既返回其输出,又返回与另一个状态对应的函数。

module Main where

a x | x == 0 = (0,b)
a x = (2*x, a)

b x | x == 0 = (0,a)
b x = (x, b)

evalstate s [] = []
evalstate s (x:xs) = (v:evalstate s' xs)
    where (v,s') = s x

main :: IO ()
main = putStrLn $ show $ evalstate a [1,1,2,3,4,0,2,3,3]

不幸的是,ab 的类型是无限的,GHC 抱怨:

Occurs check: cannot construct the infinite type: t = t1 -> (t2, t)

在 Haskell 中如何表达这种模式?

我当然可以这样做:

s 'a' x | x == 0 = (0,'b')

状态使用字符代码,但函数模式看起来更优雅。

【问题讨论】:

  • 读者的任务:让这种模式更普遍,并为它创建一个单子接口。
  • 在风格上:如果你说a 0 = (0,b)而不是a x | x == 0 = (0,b),通常会更清楚。
  • 我建议使用数据类型而不是字符代码,例如data State = A | B。但第一个也不错。
  • 深思熟虑:尝试为a 编写类型签名。有点像Int -> (Int, f),但这太模糊了。 f 是什么?它与a 的类型相同。所以整个事情现在是Int -> (Int, (Int -> (Int, Int f))) 等一下......

标签: haskell


【解决方案1】:

您正在尝试使用类型定义状态机

type SM = Int -> (Int, SM)

但 Haskell 不允许这样做。你必须使用datanewtype 来引入一个新的命名类型:

newtype SM = SM (Int -> (Int, SM))

下面是应用了这个小改动的程序,因此它现在可以编译并按预期运行:

module Main where

newtype SM = SM (Int -> (Int, SM))

a = SM a'
    where
    a' x | x == 0 = (0, b)
    a' x = (2 * x, a)

b = SM b'
    where
    b' x | x == 0 = (0, a)
    b' x = (x, b)

evalstate s [] = []
evalstate (SM s) (x : xs) = (v:evalstate s' xs)
    where (v, s') = s x

main :: IO ()
main = putStrLn $ show $ evalstate a [1,1,2,3,4,0,2,3,3]

【讨论】:

  • 具体来说,Haskell 不允许 type SM 的原因是因为它是递归的。等递归类型的类型推断是无法确定的(我认为),因此您必须在值级别引入构造函数以帮助类型检查器查看发生了什么。
  • 等递归类型推断更复杂但可确定,但允许推断等递归类型会将许多常见的编程错误转化为可类型化程序。这很烦人,因为它往往会延迟从定义函数的位置到使用它们的位置捕获程序员错误的时间。
猜你喜欢
  • 2012-02-22
  • 2021-08-05
  • 1970-01-01
  • 2012-09-29
  • 1970-01-01
  • 2015-10-14
  • 1970-01-01
  • 2011-06-28
  • 1970-01-01
相关资源
最近更新 更多