Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've thought about learning Clojure but already knowing Haskell I feel that in comparison I wouldn't gain much and then loose some.


As someone who knew Haskell first and then learned Clojure, I can tell you that this assumption is incorrect. It's so completely philosophically opposite from Haskell in so many ways that it's a very good mental exercise to really grok them both. It will make you appreciate how much you're paying for that fantastic type system. Understanding this cost will allow you to make better decisions in the future about which technologies are a better fit for a given project.


Sir, your comment is intriguing and I want to hear more about it.

What is this philosophically opposite that you mention? Haskell is good at creating domain-specific languages, so I think the differences must be around the superior meta-programming facilities in lisp languages. So, what would you do with Clojure that you couldn't do in Haskell easily?


It's an oversimplification, but the basic philosophical difference from my point of view is that Haskell values correctness above all and Clojure is above all pragmatic. I think all of these major differences in the languages are reflections of this:

- static vs. dynamic typing

- pure vs. allowing side effects

- native vs. hosted

- focus on syntax vs. rejection of syntax

Depending on the problem, you may get big benefits out of one side or the other.

One particular thing that is much easier in Clojure is dealing with heterogeneous, hierarchical data. This is just so effortless because of the combination of dynamic typing and the wonderful built-in persistent data structures. This is possible but takes a lot more work to do in Haskell and is much slower to iterate on when your data changes.


This comment is spot-on.

According to the Yegge programming axis, Haskell is extremist conservative (emphasizes safety above all) and Clojure is some notches down into a more moderate zone (merely "conservative") that balances safety against pragmatism.


I'm confused. In what sense is correctness not pragmatic?

Also, have you used lenses yet? Heterogenous hierarchical data in Haskell is quite easy now.


Guaranteed correctness has a cost in the form of extra specification needed to get things done. For example, many type errors are essentially noise: they do not catch real bugs, and they create needless busy-work for the programmer to correct them.

example:

function stringify(FooObj object): return object.toString()

Now suppose we do some refactoring somewhere, and we want to call stringify not with a FooObj but with a BarObj. Since we have defined stringify as taking a FooObj, the compiler whines until we update the definition. However, depending on your philosophy, this error is nothing more than noise, because stringify would work fine on a BarObj.

Pragmatists may wish to forgo such safety in favor of greater flexibility:

function stringify(object): return object.toString()

This is the essence of liberal programming: an emphasis on flexibility and minimal specification, at the cost of reduced safety guarantees.


That example is a straw man. First, all reasonably strong type systems support polymorphism well enough to handle that case trivially. Second, type inference is a thing, so a lot of code in strongly-typed languages even looks the same as your second code example.

I'm not saying you can't come up with a fair example to support your argument. I will say, though, that it's going to be a lot harder to do so, and that such examples only rarely come up in practice. The argument for flexibility at the cost of guarantees of correctness used to be a good one, but it has weakened significantly with time, and before long will cease to be valid at all.


The example cited makes two decisions which are not articulated explicitly but are decisions one can make and which have benefits and costs. They are:

1) The function correctly executes on the class of things for which (.toString thing) has a sensible run time invocation.

2) The function delays examination of the correct class of its argument from compile time to run time.

1 is a benefit, in that in a dynamic system it is possible that something which, at compile time, does not have a sensible .toString invocation, can gain it at run time and then participate in the function.

2 is a cost, in that you have to delay classification to the instant in which you try to invoke .toString

This tradeoff is elemental and there will be systems you can build by embracing it that will never be possible in strongly typed languages. You'll be able to build isomorphs of them, but doing so will involve expressing significantly more ideas to get there.


Terms like "straw man" imply that I'm trying to debate, but I'm not. You asked how correctness could be contrary to pragmatism, and I tried to provide an example.


Whether or not it's a debate has nothing to do with whether or not your example is valid. I was just pointing out that it isn't.


Out of curiosity, how would you define my example stringify function in Haskell with "no cost" safety around the argument type?


The typeclass that provides "toString" in haskell is "Show". It defines a few functions, of which the important one is "show", which takes the original type and returns a string. A function that is equivalent to the java-ish example is:

    stringify s = show s
No type definition is necessary, because the compiler can correctly infer the correct, most generic type:

   stringify :: (Show s) => s -> String
Or, for any s in type class Show, a function that makes s into a String. Note that dispatch is done statically.

Modern type systems eliminate most of the cost of type safety through good generics and type inference. Mainstream statically typed languages are just 20-30 years behind the state of the art.

(note that the example actually can be reduced to: stringify = show)


Guaranteed correctness has a cost in the form of extra specification needed to get things done.

This is simply untrue. Take an example in Haskell:

    > show 23
    "23"
    > show (4,5)
    "(4,5)"
    > show (Just 2.34, Nothing, [2..5], 'c')
    "(Just 2.34,Nothing,[2,3,4,5],'c')"
This works for all types which are members of the class Show. We can even derive new instances for Show for our own new data types automatically:

    > data Foo = Foo Int Float deriving (Show)
    > show (Foo 3 4)
    "Foo 3 4.0"
But what happens if we omit the instance of Show from our definition?

    > data Bar = Bar Char Bool
    > show (Bar 'a' True)

    <interactive>:12:1:
        No instance for (Show Bar) arising from a use of `show'
        Possible fix: add an instance declaration for (Show Bar)
        In the expression: show (Bar 'a' True)
        In an equation for `it': it = show (Bar 'a' True)
We get all the benefits of safety guarantees with minimal extra work. This form of automatic derivation works for many type classes in the standard libraries but in the cases where it doesn't work we simply define a few methods which are specified in the class's definition. None of this is any more than what you'd need to do in a dynamic language but you get all the extra benefits of compile time safety.


It was pretty easy before with generic traversals as well.

I think people give Haskell a bad rap for handling heterogenous data, but it's actually quite good at it. You just feel the pain of a poorly specific data domain more sharply and tend to want to reify it into something nicer quickly. No such option exists in Clojure unless you use Typed—and I say this as someone quite familiar with both languages.


I've discovered programming approaches in Clojure that I'm now looking for in other languages. For instance, two of the things I looked for straight away in Julia was if there were immutable types and map functions, since I've come to really appreciate them in Clojure. Haskell is on my list of languages to try out after Julia.


Having learnt a fair bit of Lisp, and dabbled with Haskell, it seems to me that a Haskell-like language should be implemented atop a Lisp-like language, rather than vice versa. Template Haskell puts macros atop Haskell, whereas it seems things like types and syntax should be atop Lisp.


"loosing some" is the whole point :) That's the beauty of lisps, they make "the simplest possible way of doing something" just pop out, and once you see it, you just have to do it this way! You then show the middle finger to both 'powerful type systemy abstractions' and forget you ever learned them, and then the other middle finger to 'powerfully optimized down to metal programming', and you go hunting for real world problems that you can solve using 'the simplest possible ways' thinking ...until you realize that using simple and elegant tools, you ended up building a ...type system :) (http://typedclojure.org/)


Every sufficiently advanced Lisp implementation contains an ad-hoc, badly-specified, bug-ridden implementation of half of Haskell's type system.


Until you encounter a real Lisp implementation that contains an actual thorough, well specified and bug free implementation of type system more powerful than the one Haskel has.

Interested? Then take a look at the Shen[1] and enjoy having both the benefit of types and other powerful Lisp constructs in one language.

[1] http://shenlanguage.org/


Is anyone using Shen yet? The only problem, and I am a passive lurker in circles in HN, Reddit, and elsewhere it has been mentioned, is an Ocamlesque license policy. People have said this will get them nowhere. It is understood they, from the outside view, want a single language implementation, not just a standard. But everyone I see says they will not touch it with a ten foot pole for this reason.

Also ironically, this real implementation of Lisp necessitates being built on "lesser" or not "real lisps" for us commoners, like SBCL and CLisp. It also has hosts on Ruby, Python, and this even lesser lisp called "Scheme" (rest assured, this is a joke and not a troll).

http://shenlanguage.org/Download/download.html

That being said I find it very interesting. Its licensing, even just the word if you read through, sounds very aggressive and weird.

Has anyone here used Shen yet?


Indeed, the license is impossible. In addition to what you point out, it attempts to say the same thing as many as 5 times, introducing unacceptable ambiguity.

The final straws for me is that it is the creator's explicit intent that no incomplete or incorrect implementations be publicly accessible (he among other things thinks there's enough already...), and that the author broke a promise to the community, demonstrating that his word is no good.


I am confused about all these licensing "problems" everyone is talking about here. Could you enlighten me?

Sure, Ocaml and shen have "weird" licenses but I just read through them in the last few minutes and see no major issues.

The licenses for both only effect the code for the language interpreter/compiler and associated libraries itself and should have little to no effect on your own code.

If you use MIT/BSD licenses then you are almost certainly fine, with GPL there are some potential issues but most people think that dynamic linking doesn't count as "linking" in the lawyer sense and, therefore, using GPL code with something like Shen would be fine. This hasn't been tested in the courts AFAIK.


The problem you haven't noticed is that the language covering what you care about, using Shen to write programs, is in many places (potentially at least 7 by a count I just made), and you, or, say a lawyer for your company, has to read and understand the whole huge thing to make sure your intended use is OK. And that the multiple times it tries to say the same thing are not ambiguous.

It's also explicitly not open source ("We are therefore not open source."), and should you find yourself needing to fix a bug in a language implementation your legal burden massively increases.

Compare this to the licenses you name, they're all well understood, and in at least some cases validated in court. For that matter, the GPL has been validated in at least US and German courts.

As for Ocaml, it uses the Q Public License (as well as the LGPL); it's short and a quick glance didn't indicate it tries to say the same thing more than once. It's been around for a while, to the point it's an OSI and FSF approved licence, so others have looked at it, as they did for Qt prior to version 4.0., and KDE based on it prior to then. About which there was much todo, so it's be thoroughly analyzed.

So it may be "weird", but it's significantly more palatable than Shen's license.


To qualify my original statement much later, I was told offhand by people Ocaml's multi-core processor/SMP limitations continue to exist (I know Jane Street actually is putting up money to change this) because the license of Ocaml prohibited people from making any other forked implementation of Ocaml itself. Thus, it has been single-core only for many years later, because Xavier and the core team did not think SMP implementation was a priority, and have not been asked since.

Granted, I very seemingly little about this. So I could be very mistaken. Can some confirm I am wrong? I am sure someone here has far more detail than me generalized nonsense.


Yes, it's a pity it has such a license. People tend to dismiss it without even looking at it because of the license.

I don't think the language will take off unless it change licensing before it's too late, but I still find it enjoyable to use with some smallish projects.

I hope that Shen will get a successor that will make it more suitable for modern world while keeping ideas that make it unique and sound in the first place and smoothing the rough edges.


I would love to use Shen, but I refuse. Clojure's license is restrictive enough (I can't write GPL projects in it). But Shen isn't even free software by any definition.


Uhm. Can you provide some pointers for 'Cannot write GPL projects in Clojure'? That's .. new to me.


In order to package a Clojure project, I have to include clojure.jar which is an EPL licensed project, incompatible with the GPL. If I write my code in Clojure, but license it under the GPL, then that forces me to not package clojure.jar with it, making the installation process a huge pain.


I'm pretty sure that isn't a surety. See the accepted answer here https://stackoverflow.com/questions/11412160/java-libraries-... for instance. LGPL, in any case, would almost certainly be okay. Most people I talk to consider .jar's a dynamic link and dynamic links okay via the GPL but there are alternative interpretations and I don't think it has been tested one way or another in the courts.


To the best of my knowledge you can make GPL clojure code, and include clojure.jar too.

http://osdir.com/ml/clojure/2010-03/msg01337.html


Shen has the same problem as Idris and all the rest of the languages with more powerful type systems than Haskell: none of them are ready for production use. I would absolutely love to be able to work in a more powerful type system, but the support just isn't there yet. Hopefully we'll get there in the next 5-10 years, at which point I will very happily switch over.


To be fair, Haskell had that same problem for a very long time too ;-)


It was more of a joke really. It's true in the same way Murphy's laws are true...

Still, thanks for shen, gives me something to read about.


This one has a dissertation[1] behind it. Though unread (yet), I think it's well specified.

[1]: https://github.com/downloads/frenchy64/papers/ambrose-honour...


as ashley yakeley once noted, every sufficiently well-documented lisp program contains an ml program in its comments.


One of the at least occasionally useful strengths of Clojure over Haskell is the pragmatic choice Rich Hickey made by building on top of Java. This has continued with Clojurescript (Javascript) and others like Clojure on CLI, objc, etc. What this means to me is that on the occasions when I'm faced with a task like having to produce a Microsoft Excel file readable by an array of Excel versions I can leverage the wide range of libraries available in the parent environment (Apache POI, http://poi.apache.org/). The attention paid to the facilities to interoperate with the underlying ecosystem means that even given the relatively short history of Clojure it is likely that someone has already done at least some of the work to make it pleasant to use in Clojure (https://clojars.org/org.clojars.boechat107/cloxls).


Clojure programming might be more data-oriented than Haskell programming. I've never done any real-world Haskell programming, but I'd guess it'ld have a completely different feel.

The dynamic-typing means Clojure code is usually built around data-flows of maps/vectors/strings (generic information types that are trivial to integrate from different sources), trivial serialization, code-as-data.

I'd guess Haskell code is more likely to be built around abstractions based on the more powerful type system.

clojure.core.logic - a Prolog-like logic language that you can embed in the middle of a Clojure function - is a good example of something that is possible thanks to Clojure's lisp macros.


> clojure.core.logic - a Prolog-like logic language that you can embed in the middle of a Clojure function - is a good example of something that is possible thanks to Clojure's lisp macros.

import Control.Monad.Logic

And! You don't macros for that. Macros are not used much in Haskell (only for very special cases), there's no need for them.


While useful, Control.Monad.Logic is not really an alternative to core.logic. It is a search monad more than anything, and it doesn't perform any unification or constraint propagation.

The type system gets a bit in the way when implementing something like miniKanren in Haskell. I suspect Clojure is a better fit for this kind of highly dynamic problem.


core.logic doesn't really use macros does it? Maybe to simplify the syntax, but macros is in no way required to build a core.logic library, at least not in the same way as core.async requires it.

The real beauty of macros is to simplify syntax without taking a runtime performance hit.

How would you implement thread-first or thread-last in Haskell without a runtime performance hit? Template Haskell?


Is say that the things you describe as Clojure's strengths occupy one part of Haskell's wheelhouse. The not change is that the flows orient over highly composed custom and built-in ADTs, but they're honestly hardly more painful than maps/vectors/strings while buying a lot of robustness.

And if you don't like that, it's easy to use "untyped" types as well.


Always learn another language. It opens up new mental pathways and can help you appreciate the ones you already know in surprising ways.


Well, there is no such simple interoperability with java from haskell, that's for sure. But otherwise…

  user> (:key nil)
  nil
Mmmm, I'm not certain I particularly want this

  user> (nil :key)
  CompilerException java.lang.IllegalArgumentException: Can't call nil
RUN!


As many are saying it's a pragmatic choice. And it allows you to do stuff like this:

  (:foo (:bar {}))
Without throwing a NPE. Which is what I want 99.9% of the time. As someone who codes in Clojure close to 10 hours a day, I rarely see a problem like you describe.


If you use clojure.typed, I think the second example would give you a compile-time type error.


While true, the second example already gives you a compile-time error in normal Clojure (note the CompilerException). It never compiled and was never evaluated. This on the other hand compiles and is likely what the OP wanted to show:

  user> (let [e nil] (e :key))

  NullPointerException


I envy you 'cause I'm too stupid to use Haskell. I'd suggest you stay with it if it already lets you do things. And I'm staying with Clojure 'cause it works for me.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: