2. Call-by-value gives us the ability to safely interleave effects. Now, I know you all think we should not be doing that at all, but I would say that this is only true in some cases. The point of reifying an effect in a monad is not because "effects are icky"; it is because we have written a non-extensional operation using effects, and we want it to be an extensional function. Wrapping it up in the monad "completes" the operation as such. However, there are plenty of extensional functions which may be written using computational effects (such as memoization): these should not be wrapped up in the monad. (FYI, it's the effects that preserve extensionality which is what Bob calls "benign effects", to the consternation of Haskell developers everywhere.) ML gives us the fine-grainedness to make these choices, at the cost of some reasoning facilities: more proofs must be done on paper, or in an external logical framework. I tend to think that the latter is inevitable, but some disagree.
I am hoping for a middle-ground then: something like ML, in that effects are fundamental and not just bolted onto the language with a stack of monads; something like Haskell, where we can tell what effects are being used by a piece of code.
The story hasn't been fully written on this, but I think that Call-by-push-value can help us with both recovering the benefits of laziness as well as reasoning about effects.
3. Modularity is something which Haskell people simply do not take seriously, even with the new "backpack" package-level module system they are building. One of the most-loved reasoning facilities present in Haskell depends, believe it or not, on global scope, and is therefore inherently anti-modular (this is the uniqueness of type class instances). As a result, you can never add modularity to Haskell, but we may be able to back-port some of the more beloved aspects of Haskell to a new nephew in the ML family.
(Confusingly, laziness advocates often say that their brand of functional programming has better "modularity" than strict, because of the way that you can compose lazy algorithms to get more lazy algorithms that don't totally blow up in complexity. I would say that lazy languages are more "compositional", not more "modular"—I prefer to use the latter term for modularity at the level of architecture and systems design, not algorithms.)
So memoizing data structures, or a higher order function for memoizing, utilizes a "benign effect".
So, laziness is benign in total fragments.
Syntactically it's a bit different, but from a modularity POV it is much closer to ML than Swift, Rust or Hack.
I would be very interested to hear why you think that, or just have some links on the subject. In particular it's not clear to me how CBPV helps us reason about effects.
Plus, the build/module/packaging/dependency resolution story, even when doing all the recommended best practices involving sandboxing Cabal and leveraging Nix are just plain embarrassing.
There are so many smart Haskell people. I don't understand how any of them could consider this clusterf*ck to be remotely acceptable.
Anyway, sandboxes are pointless hacks which wouldn't be necessary if things had been done properly from the beginning.
They are like Node.JS ... yes, it enables you to develop with JavaScript on the server, but how the hell did you end up with using JavaScript in the first place?!
You say "if things had been done properly from the beginning" implying you know some mistakes that were made. What mistakes were made in the beginning?
Do you have any ideas how the problems cabal has could have been avoided? There are very intelligent people working on this problem and it is well known that dependency resolution isn't an easy problem.
Maven required tons of work because it's written in Java, not because the ideas behind it are advanced in any sense.
It's sounds absurd because it's so simple, but the issue is that Cabal/GHC doesn't deal with versioning. Different versions of the same library are not properly treated as different artifacts. That's why everything breaks if you install it into a global namespace.
"Great, let's just multiply our namespaces!" is the obvious knee-jerk reaction, but not a great solution.
> There are very intelligent people working on this problem
That sounds great! Because until now I have mostly seen "very intelligent people" who have been busily in denial that a problem even exists.
ocaML or even better F#?
http://mirror.ocamlcore.org/wiki.cocan.org/companies.html
I did try to learn Ocaml at one point, but I was put off by the standard library variation (the default one apparently has numerous shortcomings, Batteries project extends it and Core project aims to replace it, but neither one is the clear winner) and the lack of extensive parallel processing support (coming soon... https://www.youtube.com/watch?v=FzmQTC_X5R4 ).
F# comes with a sensible default library (including Unicode support as default), some nice syntactic sugar (the pipe forward operator is especially useful, and to be fair it has made it into Ocaml now too... http://stackoverflow.com/questions/8986010/is-it-possible-to... ), seamless interoperability with all .NET libraries, and parallel processing is made comparatively easy (for example, if performing a map operation over a list, can just change the List.map function to Parallel.map to get the parallel version).
This is probably the F# introduction I would recommend the most (it's a video, which I know isn't ideal, but it's a good one): http://channel9.msdn.com/Blogs/pdc2008/TL11
Oh and type providers in F# blew my mind! I can't really summarise them yet, but imagine being able to mix Python or R or many other data sources in with F#. If anyone has a good introductory post on type providers, please feel free to share.
hsenv, cabal-dev, cabal sandboxes, nix, stackage ... or do you include everything in your repository (any tool to automate that)?
Haskell is actually late to the sandbox party - up to recently you couldn't use them at all, and currently they are completely optional. But now that you can, it would be stupid to start a big project without them, like in any other language.
Haskell package manager is called Cabal. Nix and NixOS are two completely unrelated projects, that not even be recommendable in a development environment.
I know cabal - with and without sandbox. I said am amazed that sandboxes or nix tools are necessary in Haskell _at all_ while every other language which uses a package manager has one that works out of the box without any hassle and without any sandbox tricks.
> Nix and NixOS are two completely unrelated projects, that not even be recommendable in a development environment.
Other Haskell developers don't agree. They prefer nix even over cabal sandboxes. Quote: "Nix helps me avoid Cabal hell" and "Nix is much better than cabal sandbox".
https://ocharles.org.uk/blog/posts/2014-02-04-how-i-develop-...
But in a language like Haskell, a sandbox is definitely a subpar solution (glacial compilation times and huge artifacts sizes inside ~/.cabal: 1GB on my machine, compared to the few hundred MB of my ~/.m2). Unless we find a way to bootstrap a sandbox from a vetted/trusted base one that contains things like Yesod (but vetting packages is a job better handled by stackage and/or nix ).
I use Nix, but I'm not comfortable yet in using it with Haskell... to try things quickly I'd need to whip up a nix-shell. But I'm more used to simply have easy access to a ghci repl at my fingertip. And if I install both things, I have to keep care at not trying to cabal install things onto the nix-installed ghc, nor to do the quick-and-dirty thing (go back to install things in ~/.cabal)
Something like lein-try [1] plus Nix might cover my use cases. But Stackage seems simpler and closer to the workflow most haskell developers are accustomed with (and it could be useful also for platforms that don't have Nix available)... if only it'd have more adoption
> every other language which uses a package manager has
> one that works out of the box without any hassle and
> without any sandbox tricks.
I have programmed professionally with C, C++, Go, Haskell, Java, Javascript, Python, and Ruby. While this is not "every other language", I feel it's a sufficient cross-section to be useful for the purposes of this thread.Trying to do any sort of open-source development without a library sandbox, in any language, is madness. OS package managers are completely unable to deal with multi-version dependency graphs. NixOS is no different, unless you want to install the cartesian product of the possible combinations -- how much SSD space do you have?
---
My experience with Haskell leads me to believe that "Cabal hell" is an artifact of certain library developers' API versioning philosophies. Namely, they release numerous libraries all depending on each other with tight version bounds, and change the API regularly. This behavior is fundamentally incompatible with dependency resolution in a compiled language.
Say you have four libraries:
foo-1.0 depends on bar==3.8 and qux==1.5
bar-3.8 depends on baz==0.2 and qux==1.5
baz-0.2 has no dependencies
qux-1.5 has no dependencies
Then tomorrow you want to release an API-incompatible version of qux and use those new features in foo: foo-1.1 depends on bar==1.1 and qux==1.6
bar-1.1 depends on baz==1.1 and qux==1.5
What do you do here? There's no good choice. Two versions of qux can't be linked into the same binary, and the tight API versioning prevents Cabal from being able to construct a coherent package set.Now imagine that instead of four libraries, it's O(50), and they're all changing regularly.
Writing lots of tooling and writing manifestos against Cabal sort of helps, for a bit, but it's a lot of work and is deeply unsatisfying for the kind of person who likes to make progress on other goals.
IMO the only practical solution is to loosen the dependency version bounds, and commit to maintaining API compatibility for several releases. I have never encountered Cabal hell when using packages with reasonable versioning philosophies.
I agree, but there're some interesting differences in how different language tools implement a sandbox.
Python and Haskell (with Virtualenv and Cabal-dev/sandbox) have separate local repo/caches in which packages are installed (this is useful if you want packages in a reliable location to install executables)
Ruby and Clojure (with Bundler and Leiningen) have a common repo/cache with multiple libraries version, and the version resolution is handled inside the project itself (e.g. when you need to whip up a repl or build the project)
(I'm not mentioning rbenv or the like, since that doesn't handle version graphs by itself... but obviously it can be used just like Virtualenv)
IMHO, the second approach is better suited for a language (implementation) that has an explicit compilation step in which an artifact/binary is built, just like for Haskell/GHC
(then again, something like the first approach is still useful for executables and maybe also to try out different compiler versions)
> Two versions of qux can't be linked into the same binary
It might be possible, but yes... it's a world of pain
http://stackoverflow.com/questions/3232822/linking-with-mult...
Pick a language where developers are not so cavalier with backward compatibility?
Java has Maven which allows you to exclude transitive dependencies for such situations, and in more than a decade of developing with the language, I've only had to use this functionality a handful of times.
Developers sometimes mess up and break backward compatibility but this should be an extremely rare event. That or the compiler itself is terribly implemented and creating incompatible binaries just by bumping up versions (looking at you Scala).
Really?
Even Java people have been perfectly fine without it. Sandboxing is just a hack around Cabal/GHC not having any workable version support.
Nix is great but you have to take some time to set it up and learn it - sandboxes are pretty standard and work as you would expect with ghc-mod (not that Nix doesn't I just had more time invested in getting it and the tooling setup to use it as a "dev environment" - cabal sandboxes are much simpler).
Stackage is great too, btw.
Do you know of any way to use multiple stackage repositories? (for now it's not a problem, but I envision a future when I'll have dozens of projects, and updating everyone of them to use the same library versions might not be feasible)
I know that I can `cabal --config-file=/path/to/cabal.config` but I'm wondering if there's an easier way and/or any convention
I'm especially worried of forgetting something (like reusing the same ~/.cabal for multiple stackage-cabal configs)
In general though, I think stuff like Stackage is a community smell and has the potential for creating a schism and confusion in the community. If it's community infrastructure I also feel like it should be controlled by haskell.org and not a for-profit company.
I know I know, money and man-power are all issues but other OSS languages have successfully figured it out and I think Haskell can too.
I personally use Nix for everyday development.
I can finally go and write some relevant code now.
I got seriously into Haskell a few months ago, but I've used ML in algorithmic trading and was a major fan.
I feel like it does us a disservice to complain about Haskell's (admittedly, warty) Exception system or "cabal hell" when what is actually going on is that we're holding the language to a higher standard. I agree that functions like head, fail, and possibly non-strict foldl, are warty; but these issues are downright minuscule when you consider what Haskell (language and community) brings to the table. (Also, every other language has warts.) The fact that our complaints about Haskell are minor annoyances compared to other languages' drawbacks, to me, signifies that the language got the major things right.
And C++ belongs to the C language family, but one could still say something like "the next great thing would come from a C rather than a C++ like language" (I'm not judging if this is true here, just that it's quite clear what it means).
It sounds like call-by-push-value gives a nice unifying approach to CBN/CBV and also effects. I am hoping that something interesting will come out of that work.
Frank's basically based on an extension of the CBPV calculus. As far as I know, Conor's also working on extending the core Frank calculus to the dependently-typed case, though this might take a bit.