First Class Statements(blog.jle.im)
blog.jle.im
First Class Statements
http://blog.jle.im/entry/first-class-statements
15 comments
This isn't exactly true. For example, the article names declaration and assignment as among "statements", but such statements are not first class in Haskell. Nor are type, data, type class statements, etc. It is true that Haskell manages to accomplish many things with expressions that you'd use statements for in other languages.
Agreed, but there's a pretty straightforward distinction between statements amenable to becoming values and those that aren't. Essentially, those which control and define the type theory are out of scope as well as those which introduce bindings.
I wouldn't call them statements because they aren't executed. Instead, I would classify them as "declarations". You state what something is, instead of telling the computer what to do.
Traditional assignment is an expression in Haskell.
Traditional assignment is an expression in Haskell.
count :: Int -> IO Int
count n = do r <- newIORef 0
loop r 1
where
loop :: IORef Int -> Int -> IO Int
loop r i | i>n = readIORef r
| otherwise = do v <- readIORef r
writeIORef r (v+i)
loop r (i+1)Somehow this doesn't seem correct. If statements were truly "first class", one should be able to programmatically put statements together and execute them (without using the loophole of simply calling the compiler).
I don't see how that's a "loophole". Your code has to tie into main somehow, but that's true of other languages too. Or you can call it from the repl. But it certainly doesn't make sense to talk about executing statements without bringing in the compiler or interpreter, in any language.
You can take a list of IO actions ("statements") and use the sequence function to get a single statement out.
The list of statements can be put together however you like.
You can take a list of IO actions ("statements") and use the sequence function to get a single statement out.
sequence :: Monad m => [m a] -> m [a]
Then you can execute the result by having it in the flow of your program, or pass it around just like any other statement.The list of statements can be put together however you like.
> one should be able to programmatically put statements together and execute them
I'm not sure I understand. In Haskell, you can programmatically put statements together. You can't execute them willy-nilly just like how you can't try to execute an integer in python. You just create make a statement called "main" and the program is compiled so as to execute that statement.
I'm not sure I understand. In Haskell, you can programmatically put statements together. You can't execute them willy-nilly just like how you can't try to execute an integer in python. You just create make a statement called "main" and the program is compiled so as to execute that statement.
To nitpick (and maybe derail this subthread a bit), you can do some pretty weird stuff with Python's numeric types. Unfortunately, the fascists who designed the language refuse to allow me to modify certain builtins...
At one point, I was desirous of having floating point numbers whose value decayed toward zero each time the number was accessed; you can achieve this by defining a custom class and implementing all the methods needed to make it behave like a float, but it's not the same.
AttributeError: 'int' object has no attribute '__call__'
I mean, of course it doesn't, that's what I'm trying to fix.At one point, I was desirous of having floating point numbers whose value decayed toward zero each time the number was accessed; you can achieve this by defining a custom class and implementing all the methods needed to make it behave like a float, but it's not the same.
Apparently Haskell does have a plugin that lets you eval expressions at runtime... from a string, though; is there a way to build an AST? (And what's the performance like?) Lisp of course would let you do something like
(do-something-with '(+ a b))
and do-something-with could do any arbitrary transformation on the AST, possibly ending up passing it to eval, possibly optimizing it and compiling it to some other language... while Haskell's a leap in this regard compared to imperative languages, I think only something like Lisp can call its code truly first-class.There's two ways in which I'd like to address your questions here.
First, directly addressing the question you asked:
There are a number of libraries for assembling and manipulating a Haskell AST - I would recommend looking at haskell-src-exts and Template Haskell. Unfortunately, there doesn't seem to be a way to exec any of these directly (at runtime). You could obviously pretty-print and then eval that, which may or may not be a good fit depending on your problem space.
Separately, I would like to suggest a different perspective, in line with the thesis of the article.
(Pardon me if this gets a bit rambly, and feel more than free to ask questions if it gets confusing - I've been meaning to expand on this in a blog post...)
Haskellers are fond of monads. Lispers are fond of S-expressions. Lisp programs are S-expressions. S-expressions are either an atom, or a list of S-expressions - a structure also known as a "rose tree".
It turns out, a rose tree is a monad. In particular, a "free monad", which means there's a unique function turning a rose tree into any other monad in a way that preserves structure (in math terms, a "monad homomorphism"). Getting slightly speculative, I think the homomorphism from Free to IO has to be Lisp's eval.
So in a senes, when Haskellers write functions that operate on "Monad m", they are writing functions that can operate on Lisp programs, and also on many "partial interpretations" or "restricted forms" of Lisp programs. This is why Haskell programmers get about as excited about monads as Lisp programmers do about macros - there's a deep connection.
The big thing that Haskell lacks, in this context, is a mutable global namespace - which keeps a few things compile-time only (and hence the need for Template Haskell for fuller coverage of metaprogramming).
First, directly addressing the question you asked:
There are a number of libraries for assembling and manipulating a Haskell AST - I would recommend looking at haskell-src-exts and Template Haskell. Unfortunately, there doesn't seem to be a way to exec any of these directly (at runtime). You could obviously pretty-print and then eval that, which may or may not be a good fit depending on your problem space.
Separately, I would like to suggest a different perspective, in line with the thesis of the article.
(Pardon me if this gets a bit rambly, and feel more than free to ask questions if it gets confusing - I've been meaning to expand on this in a blog post...)
Haskellers are fond of monads. Lispers are fond of S-expressions. Lisp programs are S-expressions. S-expressions are either an atom, or a list of S-expressions - a structure also known as a "rose tree".
It turns out, a rose tree is a monad. In particular, a "free monad", which means there's a unique function turning a rose tree into any other monad in a way that preserves structure (in math terms, a "monad homomorphism"). Getting slightly speculative, I think the homomorphism from Free to IO has to be Lisp's eval.
So in a senes, when Haskellers write functions that operate on "Monad m", they are writing functions that can operate on Lisp programs, and also on many "partial interpretations" or "restricted forms" of Lisp programs. This is why Haskell programmers get about as excited about monads as Lisp programmers do about macros - there's a deep connection.
The big thing that Haskell lacks, in this context, is a mutable global namespace - which keeps a few things compile-time only (and hence the need for Template Haskell for fuller coverage of metaprogramming).
You can programmatically put statements together, and manipulate them like real values. But that doesn't mean that pure simulated execution has to be a part of the API :)
[deleted]
[deleted](1)