I'm still working on MazesOfMonad, my Haskell RPG, now on code.haskell.org. It's actually fun to now have a working program and think about extensions in functionality, and then how to implement them in Haskell, instead of just playing around with the language.
I wanted to give the user more feedback that they currently got about what was going on in the game, and at the same time I wasn't happy with what I had written previously: I had the output messages as part of the method signature:
myFunction :: Type1 -> Type2 -> (Type3,[String])
So I decided a Monad was called for. The Writer Monad, in this case, which lets us append stuff together to get it all back at the end. I already had most of my game running in a monad transformer, so it was **just** a matter of figuring out how to wrap everything in a WriterT. I had a bit of fun at first, then got it working. So now my game runs in a
WriterT a (RandT (StateT b IO)) c
Where a b a are various types for the things I need: a is the type of messages I write, b the state I keep, and c the return value. RandT gives me random numbers, StateT gives me a state, IO allows me to do IO operations when I need to save the game or something like that.
The amazing thing, well, for me, is that a lot of my functions do not need to be aware of the whole transformer stack, but of only the type classes that they care about. My final transformer is an instance of both MonadWriter and MonadRandom. So my method that needs to write messages becomes:
myFunction :: (MonadWriter [String] m)=>Type1 -> Type2 -> m Type3
And I can write my code calling the MonadWriter methods to add messages. It's in a monad, but it's still purely functional: I return a Type3 object and write Strings. If my function needs random numbers:
myFunction :: (MonadRandom m, MonadWriter [String] m)=>Type1 -> Type2 -> m Type3
and I get random numbers capabilities!
The final code is now much cleaner than before (I actually don't use [String] directly for the MonadWriter type but an alias for it, so I can replace it with Seq String or something faster the day I fancy it). I think I now understand monads and monad type classes better, and I understand better how using a monad doesn't automatically mean using the IO Monad and lose purity. Haskell Nirvana is not far off, says he naively...
In this blog I talk about some of the personal programming I do as a hobby. From Java to Rust via Haskell, I've played around with a lot of technologies and still try to have fun with new languages and APIs!
Showing posts with label Monads. Show all posts
Showing posts with label Monads. Show all posts
Friday, May 15, 2009
Wednesday, September 19, 2007
Parser Combinator in Java
Pffuii, I'm quite busy these days, hence the posting frequency has dropped. I'm in the process of playing with a functional Java based language (more on that in later posts, hopefully) and I decided to have a go at a little parser combinator in Java.
The end result works (I can parse things like Java class declaration) and looks a lot like parsec, but there is a lot of Java cruft that I'd like to remove, things that are hidden with the wonderful magic (I don't understand it well enough to call it otherwise) of Haskell monads.
For example:
try {
Character c=new Try(new Letter()).parse(input);
...
} catch (JCParseException jpe){
...
This code tries to match a letter in the input at its current position (the input variable) and does not consume any input if it fails. I don't like having to explicitly tell the combinator to parse the input, and I don't like using exceptions to indicate a parsing failure. Now, anybody has any great ideas?
The end result works (I can parse things like Java class declaration) and looks a lot like parsec, but there is a lot of Java cruft that I'd like to remove, things that are hidden with the wonderful magic (I don't understand it well enough to call it otherwise) of Haskell monads.
For example:
try {
Character c=new Try(new Letter()).parse(input);
...
} catch (JCParseException jpe){
...
This code tries to match a letter in the input at its current position (the input variable) and does not consume any input if it fails. I don't like having to explicitly tell the combinator to parse the input, and I don't like using exceptions to indicate a parsing failure. Now, anybody has any great ideas?
Subscribe to:
Posts (Atom)