Question

A person on Reddit has brought this code to my attention:

main = do
  let ns = [print 1, print 2, print 3]
  sequence_ ns
  sequence_ $ reverse ns
  sequence_ $ tail ns ++ [head ns]
  head ns

What's going on here is we have an array of operations that we can do stuff with, like reverse or get its tail or head.

Awesome.

What I want to do is get into individual elements and change them for good. For example, I want to be able to do something like this:

ns !! 0

and get something like [print, 1] and then change last element to, say, 3.14 so that the function would print 3.14.

Is it at all possible in Haskell or should I just go back to LISP?

AN IMPORTANT EDIT: I sort of blundered. I understand that I will need to create a new list. Is it possible to get the arguments of a function, which is a part of a list? What I want is the ability to compose functions from their identifiers/arguments and also be able to break down a function into identifier/argument before it gets evaluated.

Was it helpful?

Solution

It is a bit more complicated than in Lisp, but for metaprogramming in Haskell, you can use Template Haskell.

E.g., [|print 1|] will be translated to

return $ AppE (VarE $ mkName "print") (LitE $ IntegerL 1)

which has the type Q Exp (a quotation of an expression).

If you want to splice your own data into a quotation, [|print $(foo 3.14)|] will execute foo 3.14 at compile-time.

OTHER TIPS

Once you've applied a value to a function, there's no way to get it back. Try wrapping the function and its argument in a datatype that you can evaluate or decompose depending on your needs.

data App a b = App (a -> b) a
runApp (App a b) = a b
ns = [App print 1, App print 2, App print 3]
main = do
    sequence_ $ map runApp ns
    let ns2 = [App fun (arg^2) | App fun arg <- ns]
    sequence_ $ map runApp ns2

Outputs

1
2
3
1
4
9

You want to mutate the list? You should go back to lisp ;-)

Values are immutable in Haskell. The Haskell-way is to create a new list which is equivalent to the old list except for the last element.

(There are some tricks involving monads where you can simulate mutable values and pointers, but that is probably not what you want here.)

EDIT: Not totally sure I understand the edited question, but you can handle function and argument separately as data and then "apply" later, eg.;

do
    let ns = [(print, 1), (print, 2), (print, 3)]
    sequence_ $ map (\(f,a)->f a) ns

Like has been said the haskell way is to just create a new list, but you can have mutable arrays inside the IO monad with IOArray if you really want

import Data.Array.IO

seqArr_ arr = getElems arr>>=sequence_

main= do
  arr <- newListArray (0,2) [print 1,print 2,print 3] :: IO (IOArray Int (IO ()))
  seqArr_ arr  -- prints 1 2 3
  writeArray arr 2 (print 3.14) -- change the last element
  seqArr_ arr  -- prints 1 2 3.14
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top