Próbuję zaimplementować turtle graphics w języku Haskell. Celem jest, aby móc napisać funkcję tak:Grafika żółwia jako moneta Haskella
draw_something = do
forward 100
right 90
forward 100
...
a następnie go produkować listę punktów (być może z dodatkowymi właściwościami):
> draw_something (0,0) 0 -- start at (0,0) facing east (0 degrees)
[(0,0), (0,100), (-100,100), ...]
mam wszystkie te pracujące na "normalny" sposób, ale nie udało mi się go zaimplementować jako Monady Haskella i użyć do-notacji. Kod podstawowy:
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]
Dzięki temu mogę teraz napisać
> [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]
i uzyskać oczekiwany rezultat. Jednak nie mogę uczynić tego przykładem z Monad
.
instance Monad [State] where
...
skutkuje
`State' is not applied to enough type arguments
Expected kind `*', but `State' has kind `* -> *'
In the instance declaration for `Monad [State]'
i gdybym owinąć listę w nowym obiekcie
data StateList a = StateList [State a]
instance Monad StateList where
return x = StateList [x]
uzyskać
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]
Próbowałem różne inne wersje, ale nigdy kazał mi biec, jak bym chciał. Co ja robię źle? Co rozumiem niewłaściwie?
Powinna istnieć możliwość wprowadzenia monady z Turtlegraphics, rozwijającej historię pozycji i pozycji żółwia. Ale sposób, w jaki próbujesz zaimplementować instancję 'Monad', myli typ używany do reprezentowania liczb w tej historii z typami wartości w ogóle. Twoje 'ret' i' bind' dotyczą obliczeń, które traktują tylko stany jako wartości. Ale 'return' i' >> = 'powinny wyjaśniać, jak organizować obliczenia dotyczące wartości dowolnego typu, z których niektóre mogą również rozszerzać szlak żółwia. – pigworker
Jeśli chcesz, aby "monada" generowała listę punktów, możesz potrzebować Writer + State zamiast State. Pisarz jest zwykle "monadą akumulacji", chociaż może działać w inny sposób. Aby przejść dalej, musisz również sprawdzić, dlaczego monady stanów są funkcjami owiniętymi przez "nowy typ" - są to funkcje od bieżącego stanu do pary nowego stanu plus odpowiedź, a nie tylko nowy stan. Często odpowiedzią jest 'unit' aka(). np. forward będzie typowo 'forward :: Float -> Turtle()' przy założeniu monady zwanej Turtle. –