Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

>They are the same thing in Haskell

That just raises a different problem:

y = bar(generateUUID())

z = bar(generateUUID())

>Except this one does:

It doesn't explain it in the context of real world programming.




> That just raises a different problem:

    y = bar(generateUUID()) 
    z = bar(generateUUID())
You can't write that in Haskell! Instead you write something like.

  yuuid <- generateUUID
  zuuid <- generateUUID

  y = bar yuuid
  z = bar zuuid


Yeah, I'm not denying that these things are solved in Haskell or other FP languages. People build complex applications in those languages so all things must be possible one way or another. My complaint is that the tutorials never wade into these weeds and show how FP makes real world applications easier to build.


> the tutorials never wade into these weeds and show how FP makes real world applications easier to build

Yes that's true, and it's unfortunate.


Example program to show three ways of doing that:

  import System.Random

  randomInt :: IO Int
  randomInt = randomIO

  isEven1 :: Int -> Bool
  isEven1 n = (n `rem` 2) == 0

  isEven2 :: IO Int -> IO Bool
  isEven2 n = do
      m <- n
      return $ isEven1 m

  example1 :: IO ()
  example1 = do
      i1 <- randomInt
      let result1 = isEven1 i1
      putStrLn $ show result1

      i2 <- randomInt
      let result2 = isEven1 i2
      putStrLn $ show result2

      let result2' = isEven1 i2
      putStrLn $ show result2'

  example2 :: IO ()
  example2 = do
      result1 <- isEven2 randomInt
      putStrLn $ show result1

      result2 <- isEven2 randomInt
      putStrLn $ show result2

  randomIntIsEven :: IO Bool
  randomIntIsEven = isEven2 randomInt

  example3 :: IO ()
  example3 = do
      result1 <- randomIntIsEven
      putStrLn $ show result1

      result2 <- randomIntIsEven
      putStrLn $ show result2

  main :: IO ()
  main = do
      putStrLn "Example 1:"
      example1
      putStrLn "Example 2:"
      example2
      putStrLn "Example 3:"
      example3
In example1, result1 and result2 may differ, which is signalled by the arrow (instead of "="). result2 and result2' cannot differ, which is signalled by "=", where the right hand side is verbatim the same.

In example2, result1 and result2 may differ, which is signalled by "<-" (instead of "=").

In example3, result1 and result3 may differ, which is signalled by "<-" (instead of "="). Another argument here is that they may differ, because example3 is the same as example2, except it used randomIntIsEven in place of isEven2 randomInt. But we defined randomIntIsEven to be equal to isEven2 randomInt, so result1 and result2 being able to be different in example2 but not in example3, or vice versa, would be a contradiction.


that is _not_ possible in haskell, simply. generateUUID needs a random number generator, thus it probably has a type like

  generateUUID :: IO UUID
which means that those two lines don't _really_ make sense: you'd get a compile error. to use side effects (which in this case, IO, mostly means opting into sequencing of actions) you'd have to write

  uuidY <- generateUUID
  y = bar uuidY
  uuidZ <- generateUUID
  z = bar uuidZ
in which case, equational reasoning still holds. Side-note: in haskell, you could write it while avoiding the middle line using either the left or right bind operators, so you could write it as

  y = bar =<< generateUUID
  z = generateUUID >>= bar
(mnemonically: the first one runs an action and pipes it to the left, the second one does the same but pipes to the right)

Side-note #2: that's actually not too far away from how random number generators work in haskell: either you pass the state around, or you do it in IO.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: