Hoping for a little advice: I'm trying to push for some development culture changes at work, one of which is doing more local, incremental refactors rather than large, all-at-once changes (i.e. the sort of thing for which one books 'architecture meetings'). Since we'll hopefully be massaging large parts of the codebase, there's an opportunity to jump into something new with both feet.
Question: what do you think of the thesis of pure4j [1] vis. frege [2]? Namely, are the advantages gained by switching to a haskell-like language far above those attained by simply forcing effects into a small part of the codebase?
To expand on this suggestion: Scala isn't big on purity (AFAIK the language has no concept of pure functions out of the box) but it is big on immutable data types, and mostly-pure functions arise naturally when you're working with immutable types.
Immutable types happen to be something that is familiar and comfortable to Java developers. I would therefore expect to meet a lot less resistance to "syntactic sugar and immutable classes" than "functional programming with pure functions" even if the end result is 80 percent the same.
This was the compromise our team made. Our team was having a bad time dealing with Spring. Our lead dev is a functional buff who really likes Haskell, and got us all hype about trying something new. We picked scala in lieu of Haskell because the learning curve with Haskell was a much harder sell. We avoid mutable data types unless there's a compelling reason (interop with Java libraries, for example), and most of our functions are implicitly pure. ScalaZ might have some purity constraint, idk.
5 months in with Scala and it has been a wonderful experience. Now I just have to convince our front end dev to switch over to Scala.js...
We tried the same thing, but it did not go well at all. We picked scala because people should be able to learn it easier and more gradually. But we found as people learned FP using scala, they realized how much scala held them back. We ended up redoing a PHP project in scala, then as it was 80% done redoing it again in haskell. And then from there out using haskell for everything.
Do you have any specific examples of how scala would hold y'all back in relation to haskell? I actually have trouble imagining how one could justify, in a business, scrapping an 80% complete rewrite to rewrite it again in a different language. I wonder what would be so compelling.
Scala is an OO language with FP tacked on, so it basically defaults to "make everything a pain". It was easy to justify rewriting it again because it only took two weeks to do it. A re-write is much faster than initially writing it, you're just copying yourself.
I wonder why Haskell people feel so hurt about Scala...
It's much closer to Haskell's capabilities than pretty much any other functional language.
It has better type classes, the compiler just got type inference for partially applied type constructors, it supports programming with dependent types and implicit parameters allow you to let the compiler prove almost arbitrary things.
If your definition of FP means "purity", you pretty much discarded all languages traditionally considered to be functional.
There is nothing wrong with liking Haskell more than Scala, but I think your claims are not very well-founded.
I wonder why you assume I am a haskell person, why you assume I am "so hurt about scala", and mostly why even if those two absurd assumptions were true, that you could generalize that to "haskell people".
I am obviously not a haskell person, or I would have just used haskell rather than trying scala first. I am not hurt about scala, it simply was much less productive than haskell.
Scala is not remotely the closest to haskell. Not even the closest JVM language.
And my "claims" are my personal experience, how can they not be "very well founded". You seem to be putting some emotional context into this that has nothing to do with me.
Haskell and Scala win and lose on different criteria but for type classes, I find Haskell's syntax and semantics infinitely more elegant, more terse and more flexible than Scala's. Defining type classes in Scala is a cacophony of syntax boiler plate riddled with implicits and brackets. Haskell is much more elegant in that area.
Scala's typeclasses are first-class values and they actually work as advertised.
Haskell's type classes are a mess because Haskell-the-spec (anti-modular, coherent) and Haskell-the-language (incoherent, "modular") work completely different and nobody cares and there us no real fix because both options suck, as soon as your project consists of more than one module.
It's the reason why no language after Haskell does type classes the Haskell way, and all of them do it similar to Scala's approach.
Could Scala have more syntax sugar? Sure! But people are more focused on getting them right first, before adding additional syntax.
What on earth are you talking about? Tons of other languages do type classes "the haskell way". Which is also known as "type classes". Scala doesn't have type classes, it has a close approximation.
So basically what you are saying is "Haskell has Haskell's typeclasses!".
That's a yuuuuge amount of languages that follow "Haskell"'s design! (Maybe you should start counting Haskell-to-X compilers separately to pad the number "1" a bit?)
I have already mentioned three languages that haven't followed Haskell's broken mess, while you have failed to produce even a single bit of information, while constantly crying foul at everything you don't like?
That's just amazingly hypocritical, but I guess the tactic of "blame other people for your own behaviour to make it look like tu-quoque if they respond" hasn't gotten old for you yet?
I did not read your responses to other people, since you are obviously trolling. Your three examples are nonsense, two are haskell style, and one doesn't have type classes at all (ocaml).
Ok, so if I understand you correctly: you claim that your failure to contribute anything of substance should be blamed on your inability to read and your lack of any knowledge of the topic.
The spec mandates that type class instances can only be defined together with the type class or with the corresponding data type.
While it's a very poor idea as no person except the author of the type class or the author of the data type can define a type class instance, it at least guarantees the global uniqueness and coherence of type class instances. ("There can only ever be a single typeclass instance for typeclass A and data type B.")
GHC (the only implementation that kind of matters today) takes a YOLO approach, allows as many typeclass instances as you want, doesn't bother enforcing the rules laid out in the spec.
Additionally, as type class instances are not first-class values of the language a developer doesn't even have the option to make sure he is getting the instance he expects.
There exists a GHC warning for "orphan type class instances"–but as the warning triggers on everything, not only the relevant cases where multiple instances clash and break the program–it's as useful as saying "we made sure that C's goto-fail bug can never happen again by emitting a warning for every suspicions indentation – you only have to sift through 1732 warnings until you find the dangerous one!".
Your C comparison at the end is disingenuous. It is entirely possible to use the orphan instances warning to ensure coherence in an application that contains orphan instances without a single spurious warning. Simply make a habit of putting all orphan instances in a single module, and mark only that one module -fno-warn-orphans. The rest of your code can be organized appropriately. It is not ideal, but not painful.
It's true that you can't safely define orphan instances in a library, but that also should involve no "sifting through" warnings for valid uses. It is genuinely bad practice to put orphan instances in a library, even if we had global checking - it would produce cases where you simply cannot use otherwise compatible libraries in the same project.
Your idea of centralizing things into a module assumes you have control over all code ever written.
This might be true for the 90% of Haskell that is created and abandoned by PhDs writing their theses, but if you look at other language communities: People like to use a library from time to time.
You're being deliberately obnoxious in your phrasing. Cut it out.
Regarding your content:
You are making (or missing) a point I made above, and moving goal posts.
If you are writing a library that defines a new type and would like to provide an instance of some classes, there is no problem, those are not orphans.
If you are writing a library that defines a new class and would like to provide an instance for some types, there is no problem, those are not orphans.
If you are writing an application, the approach I suggested for the context of working on an application is entirely appropriate - you write orphan instances, and have a static check that they are coherent.
There is a bit more trouble if I am a library wanting to provide an instance of a class that I do not define, for a type that I do not define. Allowing orphan instances in this case, with or without a static check for coherence, assumes consumers of my library "have control over all code ever written" - it will either be dangerous (without a check) or forbidden (with a check) that they use my library with (potentially seemingly unrelated) libraries. The correct workaround is usually to provide a newtype, for which you can define non-orphan instances. This is not bad, but it certainly is a bit of a tradeoff.
But again, you moved the goal posts. I made no general claim about the suitability of Haskell's expectation of coherence. I objected to your false claim that maintaining coherence involves picking over spurious warnings about orphan instances. It does not.
On the latter front that jvm work is a summer of code student. So on one hand it's definitely exciting work, there's definitely a bit of work between that .. Working and it being a tier one backend for ghc.
I've been haskellin at a large Megacorp environment for the past 1.5 years, with lots of smaller freelance before that.
There's a lot of different value props one can make for different tools. One pragmatic case I make is that in Haskell land it's super easy and rewarding to to engage with upstream to resolve any problems I hit. One other angle that the imvu article and I think a lot of the comments over look is that using Haskell as a shared concrete specification / engineering design substrate for communicating actually makes it much much easier to collab with colleagues on pretty crazy projects. Even if the deployed system isn't ultimately written in Haskell.
That said, with the right colleagues, having the freedom / trust to choose tools that help you best deliver is always the ideal. :)
Personally, I think you'll have more success with something more gradual than a complete syntactic overhaul. I've never used pure4j or frege but it sounds like pure4j is something you could try out incrementally, which could be quite immediately useful for your team.
I can't speak for pure4j, but I would be very wary about suggesting Frege at my workplace to (what I'm assuming are) Java programmers.
I feel like some Java programmers would have almost as much culture shock with something like Haskell as some of the php programmers at IMVU apparently did.
Fair point, but it might almost be worth it just to suggest it just to see what the opposition is like -- if there isn't much, or any, go with Frege; otherwise, fall back to pure4j. I'd imagine framing the decision as picking from two choices would improve the odds of at least one being selected.
That definitely made the short list! I picked up ocaml for a side project and enjoyed the language quite a bit. However, ocamljava is either dead or dormant at this point (last commit to master in June of last year) and it seemed to only have had one major contributor -- it would seem irresponsible to adopt such a solution in light of that.
Aah, fair point, it is pretty niche. Still, merely going off of their comparative contribution graphs [1] [2] and issue trackers [3] [4], it seems like Frege has a fair bit more throughput: for better or for worse, it seems like more _things_ are happening. Although I'll be the first to admit it's a misleading metric for determining the maturity of a project, it's still something. Imagine finding a bug in Frege: it seems like they merge even small patches from contributors, whereas ocamljava is essentially a one man show. Adopting ocamljava would probably mean forking and maintaining it too, whereas there's a possibility of just contributing to Frege.
That's all just my opinion, of course, and I hardly have enough industry experience to back up its validity! 'Just sanity checking my reasoning :)
Yes, but I have ... reservations. Same as with OCaml, I took on a small project with Scala and enjoyed the language, but only after carving out a subset of it that was palatable. Although it fits the bill, I'd be worried that enforcing conventions would become a large burden.
Unlike Frege where I (might? Frege's the only language of the three that I haven't investigated thoroughly) can point learners to essentially any Haskell tutorial and have them vaguely do 'the right thing', there exists a subset of Scala developers who use the language like Java with type inference. With Frege I could, for example, merely enforce the minimal usage of IO or State, but doing so with Scala is more complicated (maybe force the usage of cats/scalaz and their equivalent monads? It's still very easy to not use them, however). If there were an equivalent to F# on the JVM I'd settle on it in a heartbeat, but the dearth of well supported, pure functional languages makes this a tough choice. All in all, I'll probably go with pure4j, but it would be nice to stop writing Java at some point in my career.
Don't underestimate Haskell style variability--there are well-known 'schools' of Haskell writing style ... look up the Glasgow style, the Utrecht style etc. Frege, being on the JVM, adds its own twists on top of the standard Haskell syntax, from having to interop with Java classes. Essentially, it treats most Java classes as being in the IO monad because of their mutability and unbounded effects.
Yes, people can write Scala in 'Java style' or 'Haskell style' (not exactly), but I've come to think of that as a strength. Your colleagues can always start out near their comfort zone of imperative Java, and you can start out at yours, and you can slowly refactor your way towards each other. You can give Scala design talks to familiarise the team with best practices. With Haskell/Frege, you don't have that luxury. Everyone jumps in at the deep end.
About enforcing monad use: yes, it's possible to do that in Scala, but you're right that it has to be under the 'honour system'. About F#, it's not a pure functional language either. It's about as pure as OCaml and Scala.
As for pure4j, the reason I don't recommend it is that it makes it very clear that it's all in an experimental stage. So I have no idea which direction it's going.
Not relevant to this particular blog post, but I just wanted to say thank you: one of your old projects, Sphere, had a big influence on me as a young programmer (in my early teens), and is a big part of why I got into programming. SCons is also awesome. I'm glad to see you are still doing cool stuff :)
I toyed around with Sphere a bit, but RPG 6.5 was where it was at for me. I spent weeks poring over that code, and it was my first encounter with C++. I learned so much, especially from the implementation of the toolkit (tile editor, map editor, etc.). I never was able to compile it myself, though, as I didn't have access to a proper Borland C++ compiler for DOS, but that didn't matter so much as long as I could read the code and see the end result.
I'm just learning Haskell through a university course. It's quite traditional in the author's nomenclature, with extensive discussion of laziness, currying and point-free notation.
But it introduces monads very similarly to how he did it: starting with Arrays, drawing parallels to Maybes, then introducing Control.Monad.State through an assignment before moving on to IO. This has helped me a lot in grasping the concept behind monads.
Andy, the same Andy mentioned in the article, wrote a blog post in 2014 with his perspective of what it's like to use Haskell in production, if you're interested. https://engineering.imvu.com/2014/03/24/what-its-like-to-use...