9 ms·
Haskell vs. Clojure (2014)
- elliotec 10y agoClojure is beautiful and elegant. If not for it's lack of popularity and necessity of keeping up with JS constantly, it would be my most used language especially on side projects. Clojure is one of my favorite languages. If they get the stack traces/better errors figured out, it will be damn near perfect IMO.
- Royalaid 10y agoThe stacktraces are a bit verbose but still very readable
- hardwaresofton 10y agoKeeping up with JS? Maybe you're referring to clojurescript, for that criticism?
- sdegutis 10y agoThe Clojure code is shorter, but that's almost entirely because it's dynamically typed. And that's not necessarily a benefit, either. Cheshire just takes JSON and turns it into plain old Clojure data (maps, vectors, strings or keywords, etc.) whereas Aeson requires the programmer to manually unroll the whole structure from top to bottom into user-defined types. But by doing so it guarantees that if something is missing or unexpected, the code fails early and fails fast. Plus the static typing enables compile-time checking of all your code which uses it, rather than how Clojure accesses JSON which is so often stringly-typed because that's easiest/shortest/fastest.
- fbreduc 10y agohmm still unsure what the types win me then
- pseudonom- 10y agoIt's pretty handy to only have to be vigilant at the boundary layer. With the untyped approach, all consumers of the deserialized map have to guard against data which doesn't conform to expectations. With the typed approach, all subsequent functions only have to work with data conforming to the specification as laid out by types.
- alexbanks 10y agoIf your only benchmark is less code, dynamic typing is fine. This example also doesn't really exemplify the value of static typing, in that the example is a "sandboxed"(it does not have to work with external APIs, other developers, etc) "non-maintained"(once it's written it won't change over a few years requiring support) code block. An example of "Lets take 6 devs and have them build something" would probably create a more obvious use for Haskell over Clojure, as the biggest problem (to me) with Dynamic Typing is its scalability under development. As in, alone I (and most devs) could write dynamically typed code with little issue, as all decisions are made internally and guard cases can be written correctly based on those internal decisions. But throwing a second dev (or n devs) into the mix makes those internal decisions much more difficult, making the guard clauses much more difficult to write correctly. Static typing takes a lot of the guard-clause-writing out of the picture, as the compiler creates them for you.
- sgk284 10y agoIt completely eliminates a class of runtime errors from your code, while also allowing all code that consumes the data to make assumptions about the data. In the untyped code you must validate everywhere you access the data or trust that someone above you already validated it (which can break silently with any future modification).
- platz 10y agoAeson/Haskell doesn't require decoding json into user-defined data types. It supports the scenario of turning the data into a plain Map (clojure style) as well. The plain Map style is simply a less preferred paradigm in haskell, but its very easy to support this. Getting a map out from your json is a one liner.
- bitwize 10y agoIf you just want to get shit done, Clojure is fine (though any other Lisp would arguably be better than Clojure). If you want to get shit done, and make sure the types are correct, and make sure all side effects are properly managed and accounted for, that's Haskell's niche.
- Zolomon 10y ago> ... Clojure is fine (though any other Lisp would arguably be better than Clojure). Care to elaborate why you think any other Lisp is better than Clojure? I would like to hear your arguments.
- bitwize 10y agoClojure has neither the cool constructs of CL (CLOS, conditions) nor the minimal design and fantastic set of control primitives of Scheme. In fact it has no significant advantage over Scheme aside from running on the JVM, and a few disadvantages (recur, why TF are lambda lists vectors?, etc.). It's different enough to be weird and annoying, but the differences aren't advantageous enough to make me want to switch: Scheme (and CL!) has been made to run efficiently on the JVM (with some caveats, which Clojure has also!).
- xiaomai 10y agoI mostly agree with you, but I think the out-of-the-box immutable data structures and sugar around around maps are very nice compared to the out-of-the-box scheme/racket experience.
- riboflava 10y agoI'm a fan of the out of the box immutable data structures and like the sugar, though you can get both in Lisp if you want. (Presumably Racket as well.) Lisp has everything, and more, though sometimes you have to work for it. I do enjoy Clojure's interop with the JVM, but that along with the community producing interesting things (this includes ClojureScript) are the primary reasons I care about Clojure at all. On the nice-to-work-with side of things I really enjoy that it was designed by one person rather than a standards committee from decades ago, it feels fresh working with it, but if you stranded me on an island and gave me one language to have for the rest of time I'd pick Lisp for its power.
- elliotec 10y agoOne of the best Code Koans is for Clojure: http://clojurekoans.com/ http://clojurekoans.com/ Highly recommended.
- deleted 10y ago[deleted]
- deleted 10y ago[deleted]
- joobus 10y agoThat haskell implementation is less than ideal. If he uses Data.Aeson.Lens, most of that instance declaration business is not necessary. It would just be mapping over the chapter keys to create the list of lists, then sorting it.
- asciihacker 10y agoMirror of the github link please? I'm FW'ed...
- wyager 10y agoThis seems like a contrived task constructed to be convenient for Clojure.
- tombert 10y agoI'm reasonably certain I've written JSON-parsing logic in Haskell that's substantially smaller than this.
- WilliamDhalgren 10y agoI know barely enough haskell, passively, to dislike quite a lot of this code (like really, 3 lines on 3 places, just to case on a maybe to throw an error? w/a just calling fromMaybe or even fromJust? and manual instances??) - but, could someone more experienced suggest a saner one?
- andolanra 10y agoflatten :: ByteString -> [ (String, Int, Int, String) ] flatten json = case decode json of Nothing -> [] -- Type inference here can infer that we're using `val` as -- :: HashMap String (HashMap String (HashMap String String)) Just val -> let books = ["Genesis", "Exodus" {- ...and so forth -} ] in [ (book, chNum, vsNum, verse) -- for every book in the list of books... | book <- books -- find the corresponding map , Just bookMap <- [HM.lookup book val] -- for every number in the range [1..max] for chapters , chNum <- [1..HM.size bookMap] -- pull that map out of the submap , Just chMap <- [HM.lookup (show chNum) bookMap] -- ...and for every number in the range [1..max] for verses , vsNum <- [1..HM.size chMap] -- ...pull the verse out of the sub-sub map , Just verse <- [HM.lookup (show vsNum) chMap] ] Okay, this isn't as type-safe or signed-in-triplicate as the supplied Haskell solution, but if we're comparing size-to-size, then this is pretty terse while also avoiding partial functions (the supplied Haskell solution uses read, which can throw an error if we happen to have a non-numeric key in the wrong place) and still working within the restrictions of what was given.
- jeletonskelly 10y agoOne thing true of all bugs, it passed the type checker.
- codygman 10y agoThat Haskell example needs some love. There should also be a Haskell lens example. Perhaps I can help out when I get home.
- dmead 10y agoone time i made a haskell to clojure translator https://github.com/dmead/Clojure-translate https://github.com/dmead/Clojure-translate
- asa400 10y agoscala? #!/usr/bin/env amm import $ivy.`io.circe::circe-core:0.5.1` import $ivy.`io.circe::circe-generic:0.5.1` import $ivy.`io.circe::circe-parser:0.5.1` import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._ val bookOrder = Array("Genesis", "Exodus", /* and so forth */).zipWithIndex.toMap type Bible = Map[String, Map[Int, Map[Int, String]]] val bible = /* load the json file */ decode[Bible](bible).map { parse => ( for { (bookName, book) <- parse (chapterNo, chapter) <- book (verseNo, verse) <- chapter } yield (bookName, chapterNo, verseNo, verse) ) .toList .sortBy { b => (bookOrder.get(b._1), b._2, b._3, b._4) } }.foreach(_.foreach(println))
- javajosh 10y agoWell, here's a partial JavaScript solution, which doesn't use new ES2015 features and doesn't actually implement the sort. Feel free to riff: http://jsbin.com/vikesu/edit?html,js,console
- paulddraper 10y agoObviously, Scala is better than either of those languages. #!/usr/bin/env amm import $ivy.`com.typesafe.play::play-json:2.5.8` import java.nio.file._, play.api.libs.json._ val bookOrder = Seq( "Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Joshua", "Judges", "Ruth", "1 Samuel", "2 Samuel", "1 Kings", "2 Kings", "1 Chronicles", "2 Chronicles", "Ezra", "Nehemiah", "Esther", "Job", "Psalms", "Proverbs", "Ecclesiastes", "Song of Solomon", "Isaiah", "Jeremiah", "Lamentations", "Ezekiel", "Daniel", "Hosea", "Joel", "Amos", "Obadiah", "Jonah", "Micah", "Nahum", "Habakkuk", "Zephaniah", "Haggai", "Zechariah", "Malachi", "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians", "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus", "Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Revelation" ).zipWithIndex.toMap val bible = Json.parse(Files.readAllBytes(Paths.get("ESV.json"))) .as[Map[String,Map[String,Map[String,String]]]] val flattened = for((book, x) <- bible; (chapter, x) <- x; (verse, x) <- x) yield (book, chapter.toInt, verse.toInt, x) val result = flattened.toSeq.sortBy { case (b, c, v, _) => (bookOrder(b), c, v) } println(result) --- Also, Python #!/usr/bin/env python3 import json books = [ "Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy", "Joshua", "Judges", "Ruth", "1 Samuel", "2 Samuel", "1 Kings", "2 Kings", "1 Chronicles", "2 Chronicles", "Ezra", "Nehemiah", "Esther", "Job", "Psalms", "Proverbs", "Ecclesiastes", "Song of Solomon", "Isaiah", "Jeremiah", "Lamentations", "Ezekiel", "Daniel", "Hosea", "Joel", "Amos", "Obadiah", "Jonah", "Micah", "Nahum", "Habakkuk", "Zephaniah", "Haggai", "Zechariah", "Malachi", "Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1 Corinthians", "2 Corinthians", "Galatians", "Ephesians", "Philippians", "Colossians", "1 Thessalonians", "2 Thessalonians", "1 Timothy", "2 Timothy", "Titus", "Philemon", "Hebrews", "James", "1 Peter", "2 Peter", "1 John", "2 John", "3 John", "Jude", "Revelation" ] books = {book:i for i, book in enumerate(books)} with open('ESV.json') as f: result = sorted( ((b, int(c), int(v), x) for b, x in json.load(f).items() for c, x in x.items() for v, x in x.items()), key=lambda part: (books[part[0]], part[1:]) ) print(result) --- Glad we've put that to rest.
- piaste 10y ago
- piaste 10y agoIn F# you'd normally use a type provider for this kind of work, but for the sake of exercise: type Chapter = Map<int, string> type Book = Map<int, Chapter> type Bible = Map<string, Book> let bookOrder = [|"Genesis"; "Exodus"; "Leviticus"; (* etc.. *) |] let sortChapter = Map.toSeq >> Seq.sortBy fst >> Seq.toArray let sortBook = Map.map (fun _ -> sortChapter) >> Map.toSeq >> Seq.sortBy fst >> Seq.toArray let sortBible = Map.map (fun _ -> sortBook) >> Map.toSeq >> Seq.sortBy (fun (bookname, _)-> Array.findIndex ((=) bookname) bookOrder) >> Seq.toArray BIBLE_TEXT |> JsonConvert.DeserializeObject<Bible> |> sortBible |> printfn "%A" FP 101 suggests you could extract the Map.toSeq >> Seq.sortby x >> Seq.toArray function, but I don't think it gains any readability, and in any case it's just an artifact of the problem requiring an array output even though a Map would make more sense.