artemis

art "semaphore" everfree

serotonergic daydream

prismatic swarm

fractal multitudes

evershifting

theta delta ampersand

bi/pan/poly

this user is a furry


Do you talk to the computer as if it could hear you? Does it ever talk back?



you: "In haskell you can't rebind a binding to a new value the way you can in rust, because haskell is declarative. You would just get a recursive definition that would never terminate."

me, with the cheatcode to activate imperative haskell:

import Data.Functor.Identity

f = runIdentity $ do
  x <- 5
  y <- 6
  x <- x + y
  return x

main = do
  print f
vi@localhost ~> runghc whatever.hs
11

Naturally, this optimizes perfectly with constant folding:

Main_main3_closure:
        .quad   ghczmbignum_GHCziNumziInteger_IS_con_info
        .quad   11
Expanded explanation

So Identity is a basic newtype that just wraps a value, which among other things, gives you the Identity monad definition for whatever value. and thats nothing special, it just passes the wrapped value into the next chain. It's usually used in combination with other complicated shit like monad transformers. Not really important. The important thing for us is that it gives us imperative do syntax, that implicitly turns

do
  x <- 5
  return (x + 6)

into

  5 >>= (\x -> return (x + 6))

Also because of fromIntegral reasons, the 5 can get automatically turned into an Identity 5.

ANYWAYS

Identity 5 >>= (\x -> return (x + 6)) takes the 5 out of the Identity, puts it into the function, which then wraps 11 in a new Identity.

newtypes like this are free as in codegen, they dont add any runtime overhead they just add type information for the typeclass system to work. So expanding the do-notation we have

f = runIdentity $ 
  Identity 5 >>= (\x ->
    Identity 6 >>= (\y ->
      Identity (x + y) >>= (\x ->
        return x
      )
    )
  )

And expanded like this its easy to see WHY we can introduce a second x that shadows the first x, why it doesnt generate a recursive loop, and why this optimizes so well. When we fully inline >>= for Identity what we get is

f =
  (\x ->
    (\y ->
      (\x -> x) (x + y)
    ) 6
  ) 5

and this is literally just single-static-assignment form. compilers love SSA, thats what they turn everything into anyway. We already have this at the highest levels of our code before we even get to LLVM or whatever. In fact, I doubt LLVM is even responsible for the constant folding here, haskell can easily do this just by variable substitution

f = (\y -> (\x -> x) (5 + y)) 6

f = (\x -> x) (5 + 6)

f = (\x -> x) 11

f = 11

You must log in to comment.

in reply to @artemis's post: