我正在尝试在Haskell中实现海龟绘图。目标是能够编写像这样的函数:
然后让它生成一个点列表(可能带有其他属性):
我得到
draw_something = do
forward 100
right 90
forward 100
...
然后让它生成一个点列表(可能带有其他属性):
> draw_something (0,0) 0 -- start at (0,0) facing east (0 degrees)
[(0,0), (0,100), (-100,100), ...]
我已经将所有内容以“正常”的方式工作,但是我未能将其实现为Haskell Monad并使用do-notation。基本代码:
data State a = State (a, a) a -- (x,y), angle
deriving (Show, Eq)
initstate :: State Float
initstate = State (0.0,0.0) 0.0
-- constrain angles to 0 to 2*pi
fmod :: Float -> Float
fmod a
| a >= 2*pi = fmod (a-2*pi)
| a < 0 = fmod (a+2*pi)
| otherwise = a
forward :: Float -> State Float -> [State Float]
forward d (State (x,y) angle) = [State (x + d * (sin angle), y + d * (cos angle)) angle]
right :: Float -> State Float -> [State Float]
right d (State pos angle) = [State pos (fmod (angle+d))]
bind :: [State a] -> (State a -> [State a]) -> [State a]
bind xs f = xs ++ (f (head $ reverse xs))
ret :: State a -> [State a]
ret x = [x]
有了这个,我现在可以写代码了。
> [initstate] `bind` (forward 100) `bind` (right (pi/2)) `bind` (forward 100)
[State (0.0,0.0) 0.0,State (0.0,100.0) 0.0,State (0.0,100.0) 1.5707964,State (100.0,99.99999) 1.5707964]
我可以得到期望的结果。但是我无法将其变为Monad
的实例。
instance Monad [State] where
...
结果为
`State' is not applied to enough type arguments
Expected kind `*', but `State' has kind `* -> *'
In the instance declaration for `Monad [State]'
如果我将列表包装在一个新对象中
data StateList a = StateList [State a]
instance Monad StateList where
return x = StateList [x]
我得到
Couldn't match type `a' with `State a'
`a' is a rigid type variable bound by
the type signature for return :: a -> StateList a
at logo.hs:38:9
In the expression: x
In the first argument of `StateList', namely `[x]'
In the expression: StateList [x]
我尝试了各种其他版本,但始终无法按照我的意愿运行。我做错了什么?我理解错误了什么?
Monad
实例的方式混淆了用于表示历史中数字的类型与一般值的类型。你的ret
和bind
只涉及将状态作为值进行计算。但是return
和>>=
应该解释如何组织涉及任何类型值的计算,其中一些可能也会扩展海龟的路径。 - pigworkernewtype
包装的函数-它们是从当前状态到新状态加答案对的函数,而不仅仅是新状态。通常答案是unit
即()。例如,假设有一个叫Turtle的单子,那么forward通常会是forward :: Float -> Turtle ()
。 - stephen tetley