3 ms·
Maybe the REPL behaves different than the interpreter? Haskell REPLs tend to work the same way. GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
by Widdershin 6y ago
Maybe the REPL behaves different than the interpreter? Haskell REPLs tend to work the same way.
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
Prelude> a = 5
Prelude> a = 7
Prelude> print a
7
But yeah, it doesn't seem like there's any support for first class functions either, so perhaps just a bit too much creative license in marketing?
- nv-vn 6y agoThis is doing something different though. It first declares a as a num, then assigns it. There's not really a great equivalent in Haskell syntax, but in C: int a = 5; a = 7;
- eyelidlessness 6y agoI have never worked with Haskell but admire it from afar, and I'm honestly astonished that the REPL works this way. I would assume this would error at the compiler for a normal program, and that the REPL runtime would enforce the same constraints.
- eyelidlessness 6y agoI guess an argument can be made that forcing a restart of the REPL is a pretty harsh punishment, but I would expect Haskell (of all languages) to at least require some kind of tabula rasa.
- Widdershin 6y agoIf it makes you feel better node doesn't allow you to reassign const declarations in the REPL, which is actually very annoying if you forget about that behaviour and tend to default to writing const.
- nimih 6y agoYou're just shadowing the previous binding with a new one, not actually mutating anything[1]: λ> let foo = 1 λ> let foo = 2 λ> foo => 2 could be rewritten[2] as λ> let foo = 1 in ( let foo = 2 in foo ) => 2 which makes the semantics more clear. This idiom is also quite common in Clojure, another famously immutable-first language: (let [foo 1 foo 2] foo) ;; => 2 In my opinion (predominantly informed by my experience with those two languages contrasted with the usual suspects from mutable-OOP-land), the benefit of immutability isn't what's happening in your own local scope, since it's typically quite easy to track what your immediate context is doing to a variable. Instead, it's the language-enforced promise that the only changes to local values can come from local actions: your function and method calls can never have spooky side effects, nor can other threads if you're in some hellish concurrent environment. In this light, Chaos's apparent syntactic sugar of a[15] = 44 to mean (using some hand-wavey pidgin) a = a.updateAt(15,44) seems quite reasonable and fully in the spirit of immutability as a meaningful language feature. [1] In my scratchwork project, my repl actually yells at me with an annoying warning about this: <interactive>:8:5-7: warning: [-Wname-shadowing] This binding for ‘foo’ shadows the existing binding defined at <interactive>:6:5 [2] I believe that the haskell repl actually functions like a do-block, so the pedantically correct desugaring possibly involves lambdas and bind, but that's not really an interesting distinction here IMO and makes the example less clear.
- imtringued 6y ago> the benefit of immutability isn't what's happening in your own local scope, since it's typically quite easy to track what your immediate context is doing to a variable. From my experience any sufficiently badly written code is hard to understand even in the local scope. The bar for hard to understand code isn't that high because the complexity of well written code is very close to zero. Most code that avoids reassignment can be read from top to bottom. Code with local mutation can require backtracking and then the complexity can start exploding but that doesn't necessarily apply to code with mutation across functions. A simple list.add() doesn't cause anyone's brain to melt. A classic is that someone writes a for loop like this: for(;i < 10; i++). You now need to go back and check what the start value of i is even though 99% of the time it is 0. Then you need to check if the counter is used for anything after the loop has exited because sometimes a loop is just trying to find the first element that matches a predicate. Now imagine if the i variable gets reused by a second loop. You now must check if the i variable is resuming the loop at the same position by backtracking. Every single line of code has the potential to amplify the complexity of the entire function. All that meaningless complexity has no reason to exist. It doesn't provide any value and it doesn't cost anything to avoid it.