Simple Interpreter with State Monad

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Simple Interpreter with State Monad

Jake
I'm currently in a graphics class where, in order to provide a standard interface to all of our graphics libraries, we have to process small scripts that look like this:

line
0 0 0 1 1 1
circle
0 0 10
scale
0 0 3
save
pic.png

I successfully wrote a parser with attoparsec that parses the file into a list of Commands. Now I'm trying to process that list to produce an IO action, and I thought the State monad would be useful to keep track of the persistent state of the shapes to draw and the transformations on them.

I'm confused about how exactly to do this though. What should the type of my State be? Right now I have an execute function that is
execute :: Command -> State ParseState (IO ())
where ParseState is a tuple of stuff. ParseState also includes an IO () because I wanted to be able to create multiple pictures one after another, and I couldn't figure out how to access the previous result value from State to add on to it in the next one.

So can anyone offer advice on my specific situation, or maybe a simpler example of how to go about writing an interpreter with the State monad?

Thanks,
Jake

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Simple Interpreter with State Monad

Andrey Chudnov
First, if you want to use several monads at a time, you should use monad transformers (see the 'mtl' package). Then you could type your interpreter function as 'execute :: Command -> StateT ParseState IO ()'

Second, ParseState including IO sounds like poor design to me (basically, in your monad you will end up carrying two pieces of IO). I can't explain how to do this better, because you haven't provided the definition of ParseState and execute.

As for examples, a quick search revealed this extensive answer on StackOverflow: https://stackoverflow.com/questions/16970431/implementing-a-language-interpreter-in-haskell#16971570

For your particular case, you might want to look at free monads, as they may offer a cleaner way to implement what you want.  http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html

On 04/07/2016 10:29 AM, Jake wrote:
I'm currently in a graphics class where, in order to provide a standard interface to all of our graphics libraries, we have to process small scripts that look like this:

line
0 0 0 1 1 1
circle
0 0 10
scale
0 0 3
save
pic.png

I successfully wrote a parser with attoparsec that parses the file into a list of Commands. Now I'm trying to process that list to produce an IO action, and I thought the State monad would be useful to keep track of the persistent state of the shapes to draw and the transformations on them.

I'm confused about how exactly to do this though. What should the type of my State be? Right now I have an execute function that is
execute :: Command -> State ParseState (IO ())
where ParseState is a tuple of stuff. ParseState also includes an IO () because I wanted to be able to create multiple pictures one after another, and I couldn't figure out how to access the previous result value from State to add on to it in the next one.

So can anyone offer advice on my specific situation, or maybe a simpler example of how to go about writing an interpreter with the State monad?

Thanks,
Jake


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Simple Interpreter with State Monad

Holger Siegel
In reply to this post by Jake
Am Donnerstag, den 07.04.2016, 14:29 +0000 schrieb Jake:

> I'm currently in a graphics class where, in order to provide a
> standard interface to all of our graphics libraries, we have to
> process small scripts that look like this:
>
>
> line
> 0 0 0 1 1 1
> circle
> 0 0 10
> scale
> 0 0 3
> save
> pic.png
>
>
> I successfully wrote a parser with attoparsec that parses the file
> into a list of Commands. Now I'm trying to process that list to
> produce an IO action, and I thought the State monad would be useful to
> keep track of the persistent state of the shapes to draw and the
> transformations on them.
>

> I'm confused about how exactly to do this though. What should the type
> of my State be? Right now I have an execute function that is
> execute :: Command -> State ParseState (IO ())
> where ParseState is a tuple of stuff. ParseState also includes an IO
> () because I wanted to be able to create multiple pictures one after
> another, and I couldn't figure out how to access the previous result
> value from State to add on to it in the next one.
>
>
> So can anyone offer advice on my specific situation, or maybe a
> simpler example of how to go about writing an interpreter with the
> State monad?

What do you mean by 'persistent state of the shapes'? The canvas itself?

You have an operation that applies a command to a canvas:

  evalCommand :: Canvas -> Command -> Canvas

But you also want to save intermediate results, so the result has to be
an IO action:

  runCommand :: Canvas -> Command -> IO Canvas

You also have an empty initial canvas:

  emptyCanvas :: Canvas

And finally you supply the parsed list of commands:

c :: [Command]

Now you want to calculate a value of type (IO Canvas) from the above:

runAll :: [Command] -> IO Canvas
runAll c = foldM runCommand emptyCanvas c


I don't see how the state monad can make things easier here.


_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Simple Interpreter with State Monad

Graham Klyne-2
In reply to this post by Jake
On 07/04/2016 15:29, Jake wrote:> I'm currently in a graphics class where, in
order to provide a standard
 > interface to all of our graphics libraries, we have to process small
 > scripts that look like this:
   [...]

I did something like this for my Swish system, some time ago.

Several others adapted the software, and most recently Doug Burke put the code
in BitBucket.

Relevant modules are

"Scrips.hs" - originally a "parsec" based parser, but now something else, parses
script source and returns a "SwishStateIO" monad.

-
https://bitbucket.org/doug_burke/swish/src/8f72b52f02fb540e8e1c26caa5036920a9ff31da/src/Swish/Script.hs?at=default&fileviewer=file-view-default

"Monad.hs" - defines the "SwishStateIO" monad, and defines implementations of
the primitive functions of the scripting language.  It's a long time since I
looked at this, but I think a key part of the definition is:

     type SwishStateIO a = StateT SwishState IO a

which (if I get this right) wraps the basic state and associated transformation
functions in an IO monad.

-
https://bitbucket.org/doug_burke/swish/src/8f72b52f02fb540e8e1c26caa5036920a9ff31da/src/Swish/Monad.hs?at=default&fileviewer=file-view-default

The "compiled" script (SwishStateIO value) is run by applying it to an initial
state; the module that pulls it all together is:

-
https://bitbucket.org/doug_burke/swish/src/8f72b52f02fb540e8e1c26caa5036920a9ff31da/src/Swish.hs?at=default&fileviewer=file-view-default

...

I'm not sure if any of this makes any sense, but the key idea for me, way back
when, was the capability to compile a script directly to a monadic function that
could then be executed by function application to an initial state (in this
case, using execStateT).

#g
--


On 07/04/2016 15:29, Jake wrote:

> I'm currently in a graphics class where, in order to provide a standard
> interface to all of our graphics libraries, we have to process small
> scripts that look like this:
>
> line
> 0 0 0 1 1 1
> circle
> 0 0 10
> scale
> 0 0 3
> save
> pic.png
>
> I successfully wrote a parser with attoparsec that parses the file into a
> list of Commands. Now I'm trying to process that list to produce an IO
> action, and I thought the State monad would be useful to keep track of the
> persistent state of the shapes to draw and the transformations on them.
>
> I'm confused about how exactly to do this though. What should the type of
> my State be? Right now I have an execute function that is
> execute :: Command -> State ParseState (IO ())
> where ParseState is a tuple of stuff. ParseState also includes an IO ()
> because I wanted to be able to create multiple pictures one after another,
> and I couldn't figure out how to access the previous result value from
> State to add on to it in the next one.
>
> So can anyone offer advice on my specific situation, or maybe a simpler
> example of how to go about writing an interpreter with the State monad?
>
> Thanks,
> Jake
>
>
>
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Reply | Threaded
Open this post in threaded view
|

Re: Simple Interpreter with State Monad

Jake
Thanks to everyone for the suggestions, I'm reading up on Monad transformers and I'll see where that takes me.

Graham, I was also curious why your parsers return SwishStateIO ()? When I was thinking about the design, it made sense to me to separate the parser from the interperter, that is my parsers return Command, where Command is defined like this:

data Command = Line D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord
             | Circle D3Coord D3Coord D3Coord
             | Hermite D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord
             | Bezier D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord D3Coord
             | Identity
             | Scale D3Coord D3Coord D3Coord
             | Translate D3Coord D3Coord D3Coord
             | RotateX D3Coord
             | RotateY D3Coord
             | RotateZ D3Coord
             | Apply
             | Display
             | Save FilePath

And then, my interpreter will consume a [Command] and produce some sort of Monad. What are the advantages of doing it your way?

Thanks,
Jake

On Thu, Apr 7, 2016 at 1:05 PM Graham Klyne <[hidden email]> wrote:
On 07/04/2016 15:29, Jake wrote:> I'm currently in a graphics class where, in
order to provide a standard
 > interface to all of our graphics libraries, we have to process small
 > scripts that look like this:
   [...]

I did something like this for my Swish system, some time ago.

Several others adapted the software, and most recently Doug Burke put the code
in BitBucket.

Relevant modules are

"Scrips.hs" - originally a "parsec" based parser, but now something else, parses
script source and returns a "SwishStateIO" monad.

-
https://bitbucket.org/doug_burke/swish/src/8f72b52f02fb540e8e1c26caa5036920a9ff31da/src/Swish/Script.hs?at=default&fileviewer=file-view-default

"Monad.hs" - defines the "SwishStateIO" monad, and defines implementations of
the primitive functions of the scripting language.  It's a long time since I
looked at this, but I think a key part of the definition is:

     type SwishStateIO a = StateT SwishState IO a

which (if I get this right) wraps the basic state and associated transformation
functions in an IO monad.

-
https://bitbucket.org/doug_burke/swish/src/8f72b52f02fb540e8e1c26caa5036920a9ff31da/src/Swish/Monad.hs?at=default&fileviewer=file-view-default

The "compiled" script (SwishStateIO value) is run by applying it to an initial
state; the module that pulls it all together is:

-
https://bitbucket.org/doug_burke/swish/src/8f72b52f02fb540e8e1c26caa5036920a9ff31da/src/Swish.hs?at=default&fileviewer=file-view-default

...

I'm not sure if any of this makes any sense, but the key idea for me, way back
when, was the capability to compile a script directly to a monadic function that
could then be executed by function application to an initial state (in this
case, using execStateT).

#g
--


On 07/04/2016 15:29, Jake wrote:
> I'm currently in a graphics class where, in order to provide a standard
> interface to all of our graphics libraries, we have to process small
> scripts that look like this:
>
> line
> 0 0 0 1 1 1
> circle
> 0 0 10
> scale
> 0 0 3
> save
> pic.png
>
> I successfully wrote a parser with attoparsec that parses the file into a
> list of Commands. Now I'm trying to process that list to produce an IO
> action, and I thought the State monad would be useful to keep track of the
> persistent state of the shapes to draw and the transformations on them.
>
> I'm confused about how exactly to do this though. What should the type of
> my State be? Right now I have an execute function that is
> execute :: Command -> State ParseState (IO ())
> where ParseState is a tuple of stuff. ParseState also includes an IO ()
> because I wanted to be able to create multiple pictures one after another,
> and I couldn't figure out how to access the previous result value from
> State to add on to it in the next one.
>
> So can anyone offer advice on my specific situation, or maybe a simpler
> example of how to go about writing an interpreter with the State monad?
>
> Thanks,
> Jake
>
>
>
> _______________________________________________
> Haskell-Cafe mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>
_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

_______________________________________________
Haskell-Cafe mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe