On Friday, September 16, 2016 at 3:42:48 PM UTC+2, Kosyrev Serge wrote:Chris Kahn writes:
> I would like to second this thought. Using Haskell for package
> descriptions needs to be thought out and executed with great care and
> attention. It's really easy to go off the rails.
>
> Scala's build system lets you do very powerful things, but it also
> makes things unnecessarily complicated and mystifying for beginners.
> At my previous work where we used Scala extensively, there were many
> times where the team simply resorted to external tools because
> figuring out how to make some seemingly trivial change to an SBT
> module was too time consuming.Let me guess (have no idea about sbt) -- unbridled Turing completeness?
Declarativity is king for configuration, and Turing completeness ain't it --
please, see my other mail about subsetting Haskell.
That's not the main problem with SBT. How do I explain it? Take this as an example of what Haskell should *not* do.
# SBT made difficult
Look, we all know a monad is just a monoid in the category of endofunctors, right?. Now, a SBT build configuration is just a heterogeneously-typed map from keys to monadic values that can be evaluated to a graph of setting transformers and build actions, so what's the problem?
And oh, I forgot to mention keys aren't simple strings but have a hierarchy themselves, and this hierarchy is used for inheritance and overriding of settings (nothing as simple as OO inheritance, mind you, think of something like CSS but different).
Isn't using Haskell supposed to require a PhD? So why would its build tools use something so simple as nested records, like Cabal does?
</sarcasm>
I think I'm trolling, but the above is somewhat accurate (except for any misunderstanding of SBT I might have)—I personally enjoy using SBT and its power, and once you learn it can be reasonably easy, but I think Kmett's lens library might be simpler to learn.
In fairness, many SBT builds can be read without having any clue of the above, because they look like imperative programs. But as soon as you need to do a bit more or you make a type error, you end up facing some of the above complexity—if you want, the "imperative program" abstraction is extremely leaky.
For instance, here's something "easy" (but count the amount of custom symbolic operators):
scalaVersion := "2.11.0"
scalacOptions += "-deprecation"
libraryDependencies += org.scalatest" %% "scalatest" % "2.0"
Then you want to use one setting when defining another, and suddenly you end up with:
libraryDependencies <+= scalaVersion (ver => "org.scala-lang" % "scala-compiler" % ver)
Luckily, this can be done more easily nowadays, thanks to Scala macros O_O.