Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Haskell, How user adds an item to a list

Tags:

haskell

I understand how to use recursive data structures to manage a list of thingies:

data Thingy a = NoThingy | Thingy a a (Thingy a) deriving (Show, Read)

firstThingsFirst :: a -> a -> (Thingy a)
firstThingsFirst a b = Thingy a b NoThingy

andAnotherThing :: a -> a -> Thingy a -> Thingy a
andAnotherThing a b NoThingy = Thingy a b NoThingy
andAnotherThing a b things = Thingy a b things

And in ghci I can do something like:

let x=andAnotherThing "thing1" "thing2" NoThingy
let y=andAnotherThing "thing3" "thing4" x

However, I don't know how to make this work for a compiled program which takes user input. In other words, I want to let a user fill up the structure. Something like:

import System.IO
allThings=NoThingy
main = do
  putStrLn "First Thing"
  first<-getLine
  putStrLn "Second Thing"
  second<-getLine
  let allThings=Thingy first second allThings
  print allThings
  main
like image 848
boawk Avatar asked Dec 06 '22 18:12

boawk


2 Answers

Values in Haskell are immutable, so if you "add an item to a list", you get a new list. So in your code above, the

let allThings = Thingy first second allThings

doesn't do what you expect. The top-level allThings has the value NoThingy and that cannot change. The name allThings in the let-binding doesn't refer to the top-level entity, it introduces a new binding shadowing the top-level name, and that new name is also referenced on the right hand side of the binding. So that line and the following are equivalent to

let theThings = Thingy first second theThings
print theThings

The let-binding creates a cyclic structure, referring to itself as one of its components. That means of course that printing it will never finish.

What you (probably) want to do requires passing the structure you want to update as a parameter

loop things = do
        putStrLn "First Thing"
        ...
        let newThings = Thingy first second things
        print newThings
        loop newThings

And of course, like Nicolas said, you probably want to convert the input strings to values of appropriate type.

like image 178
Daniel Fischer Avatar answered Dec 09 '22 15:12

Daniel Fischer


What you create is an infinitely self-referential "allThings" that gets printed.

You are binding to the name allThings twice. The first time before main and the second time before print.

The second binding refers to allThings at then end of the right hand side. This reference is NOT to the first binding. This reference is to the second binding itself.

If you change the name of the second binding and of the print:

main = do
  putStrLn "First Thing" 
  first <- getLine
  putStrLn "Second Thing"
  second <- getLine
  let allThings2 = Thingy (read first) (read second) allThings
  print allThings2
  main

then you will get a single Thingy printed on each loop of main. Since you want to accumulate the answers you can define a tail-recursive "query" like this:

query old = do
  putStrLn "First Thing"
  first<-getLine
  putStrLn "Second Thing"
  second<-getLine
  let new=Thingy first second old
  print new
  query new

main = query NoThingy

The above may do what you are looking for.

like image 39
Chris Kuklewicz Avatar answered Dec 09 '22 14:12

Chris Kuklewicz