BODOL – inspired by Haskell, Clojure and Shen
github.com
github.com
Though BODOL mentions static typing, I didn't see any examples... What did I miss?
Why? Too limiting, syntax too cumbersome, transformers suck?
do
f <- openFile 'somefile.txt'
r <- read f
s <- doSomethingToR r
Kinda looked like every other language. Except no one else spoke it and the libraries (at the time) was of highly varying quality..Also, while I was comfortable with monads, I never got comfortable with monad transformers. I had one monad that was a stack of something like 4-5 monad transformers and I started to feel as though I was losing touch with the beauty of Haskell.
print sum(float(line) for line in open('somefile.txt'))If type safety, then in my opinion it is overrated. A side effect of using dynamically-typed language is that developers keep their naming conventions consistent and in check. Because that's the only sane way to avoid stupid typos in a language that doesn't have a compiler. It is not so in the compiled language world. What I often see in the code, where type safety is enforced, - is that developers get sloppy with names! And as a result, there is a significant increase in bugs caused by logic errors. sad
Performance, type safety or compile errors?
Yes, yes and yes. A side effect of using dynamically-typed language is that developers
keep their naming conventions consistent and in check.
I've not found this to be the case. I just discussed method naming in Javascript with a developer today while I was reviewing his code. He's a very good developer and this is an A+ codebase (no, it's not a pile of jQuery plugins...). But he hadn't really thought about how to name some methods and didn't realize the confusion that would be caused in 6-18 months. We left it as he had written it because it would have been a pain to hunt down the dependencies.Were we working in a compiled world, we would have refactored and the compiler would have caught any missed dependencies in a second.
What I often see in the code, where type safety is enforced, -
is that developers get sloppy with names!
I've definitely not seen that. I maintain the same rigor w.r.t. hygiene whether I'm working in statically or dynamically typed languages, but I really miss the compiler when I'm working in dynamic languages.At the risk of sounding really rude, can I ask whether your anecdotes are informed by work with competent developers?
We left it as he had written it because it would have been a pain to hunt down the dependencies. Were we working in a compiled world, we would have refactored and the compiler would have caught any missed dependencies in a second.
Ideally, unit tests should take compiler role and be able to catch typos and missing dependencies. There is really no excuse for not maintaining unit tests in the dynamic world.
Unless it is a research grade code with limited lifetime.As to your question, I have no idea really. I've stopped judging goodness of other developers and myself some years back.
What I was trying to say, is that you can catch missing dependencies and misspellings for free, while running lightweight unit tests. (unit tests are there for catching logic errors and allowing debugging of individual modules; catching misspellings is just a side effect).
On a side note, if somebody is complaining that "a typo in the python code caused client call at 2AM, this could have never happened in Haskell", what they are really saying is that they haven't bothered to run any test coverage whatsoever before deploying new code.
Sure, but a lot of code doesn't need that. Say you're writing a conversion between two datatypes; in something like Haskell you can be confident that if it compiles then it's correct. There's no logic for there to be errors in, so having to write a unit test would be an additional overhead in a more weakly typed language.
>On a side note, if somebody is complaining that "a typo in the python code caused client call at 2AM, this could have never happened in Haskell", what they are really saying is that they haven't bothered to run any test coverage whatsoever before deploying new code.
Bollocks. You can have 100% line coverage and still hit a type error. And a language where you have to have 100% coverage is a lot less pleasant to work in than one where you don't.
For example in [1] it is described how a type system can be used to help you keep track which kind of string contains what data (SQL, XML, user input...) to catch problems with injection attacks at compile time.
[1] http://blog.moertel.com/posts/2006-10-18-a-type-based-soluti...
Still. Yes. Your point is certainly valid.
Type-safety of the Java kind might be overrated. Type-safety of the Haskell kind, though is hard to over-rate. It catches the vast majority of bugs, including ones people would refer to as "logic bugs".
- this library allows to use Scala-style underscore arguments in Python code (and much more).
print =<< sum . map read . lines <$> readFile "somefile.txt"
Someone can probably golf this shorter, though. =) print . sum . map read . lines =<< readFile "somefile.txt" ((print . sum . map read . lines) <=< readFile) "somefile.txt" sumFileLines = print . sum . map read . lines <=< readFile
Less so inline because of the excessive parentheses or ($).Either one is all right, but personally I prefer list comprehension notation.
As per Guido: ... and that a more concise notation for anonymous functions would make map() more attractive. Personally, I disagree—I find the list comprehension notation much easier to read than the functional notation, especially as the complexity of the expression to be mapped increases. ...
As far as map vs. list comprehension, yes, I prefer the comprehension in the cases where map would need lambda. Here that's not the case. Per "as the complexity of the expression to be mapped increases", this expression 'float' is the simplest possible.
Here's a reddit post I wrote about it a few months ago: http://www.reddit.com/r/programming/comments/14hhwa/program_...
Imperative code is much more intuitive in the small. That's why we lose when we make side-by-side code snippets of small toy programs and call it a case for FP. The 25-line imperative method is often more intuitive than the highly-dense 6-line functional one. The problem is that imperative code composes very poorly.
The 25-line functional program will typically include a 6-line imperative one (with all that imperative code in one place if it's Haskell).
In a single threaded, deterministic environment yes. But when you're talking multithreaded, I'd disagree.
if (user != null)
user.company = getCompanyByName("google")
being a NPE waiting to happen (in a multithreaded environment) is certainly non-intuitive.Also, a 5 layer transformer stack is typically wrapped in a newtype, so it looks like an ordinary monad with the operations you need.
doSomethingToR <=< read <=< openFile
instead. (IDENTIFICATION DIVISION.
(PROGRAM-ID. HELLO-WORLD.)
(PROCEDURE DIVISION.
((DISPLAY 'Hello, world'.)
(STOP RUN.))))Essentially, COBOL allows programmers to declare what their input file looks like and the compiler generates code that does all of the work of parsing the input and getting the data into the relevant variables.
Because COBOL comes from the (Big) Iron Age, this is all in terms of record-oriented files with fixed-width fields; a modern re-implementation of the concept would have to operate in terms of nested data structures (XML, JSON, sexps, etc.), which a Lisp-like's syntax is well-equipped to describe anyway.
If the features are good then I don't care about the name. I really like Rust (http://www.rust-lang.org/) although it sounds "rusty".
As for the name, if clojure was named anything that had the word "lisp" in it, like "foo lisp" or the like, it would have been dead already. Even racket had to get rid of the word "scheme" from their name for marketing reasons.
I studied computer science in the 80's. Maybe that there were some business ads of Lisp (especially Lisp machines). But I had the strong impression that Ada was much more hyped than Lisp. Where is Ada today?
> if it was features, then smalltalk would have won
It depends on how "features" is defined. C won over Lisp and Smalltalk because of the single feature of performance. At the time when C was invented hardware was very expensive.
> Even racket had to get rid of the word "scheme" from their name for marketing reasons.
Racket is a different language. Scheme (R6RS etc.) is a subset of that.
According to your logic Rust will have no chance at all. We will see.
One big thing to consider about the normal Lisp and Smalltalk implementation style vs. C and Clojure by virtue of it running on top of the JVM (and CLR, and for the Javascript version I hope it inherited a lot of this) is that the former are "take over the world" approaches, back in those days and still a lot today (e.g. SBCL) implemented by dumping an image to memory and restoring that when you want to run it.
Whereas C is very much a building blocks approach, and within the JVM Clojure is as well. E.g. for the latter the popular Leiningen tool uses directives like
:dependencies [[org.clojure/clojure "1.4.0"]
[clj-time "0.4.4"]
[etc.]
And known repositories to pick up the corresponding .jar files. Java and any other language that fits into the JVM ecosystem is an equal player on the JVM with Clojure, and it defers to Java for things the latter already does quite well enough.This sort of approach, which in the case of Java and Clojure is NOT Worse is Better/The New Jersey Way, seems to have survival characteristics.
Hard to determine how much this was a factor vs. performance, C being a general purpose portable assembler. After UNIX(TM) and C grew up on tiny machines, we then largely squeezed into barely larger ones, the 8086 and 68000 based ones when DRAM was still pretty dear. Or for "engineering workstations", I'm told a non-common, although not all that good configuration early on was one Sun with a hard drive and 2-3 diskless ones all sharing the drive.
The name made me chuckle.
The Hindley-Milner actually makes seeing where the type system comes in a little difficult with these examples.
Hopefully see more code soon.
I spent 2-4 weeks really trying hard to learn Haskell and I loved it. But when I tried to make something practical with it, I got so frustrated with its dependency manager (cabal) and its lack of support for Windows that I dropped it for Clojure.
I love Clojure's ecosystem, its ability to use any Java library, and its (lack of) syntax.
But I really miss the language features of Haskell. I think Clojure lets you get away with a lot of imperative style programming. You can have a function deep down in the bowels of your code perform side effects if you want. Haskell makes you define these in one place and forces you to separate it from the rest of your code (at least that's what the beginners books led me to believe).
A language that combines the benefits of both sounds like a dream.
My first project attempt was to write a web crawler that processed the data it downloaded. There seemed to be a library that was PERFECT for that named shpider: http://hackage.haskell.org/package/shpider
But, I couldn't install it because it depends on the curl library and the curl library depends on linux binaries. I develop on Windows. The people in the IRC channel kept on telling me the path of least resistance is to switch my OS. But I'm not willing to do that, especially if it's just for a practice project.
I was told I should be using http-conduit (or something) instead. But that was lower level than shpider so not ideal. I tried to install that and I got an error, too. Apparently the project owner had an incorrect config file and it was trying to use a version of the fault library that it was incompatible with (he fixed that the next day which was great. But the only reason I could have figured out my problem is with a team of people on IRC holding my hand through it). I can't blame Cabal for that, but for a complete newbie who has no idea what's going on, it's extremely intimidating.
Long story short, I tried installing ~10 libraries with Cabal and only one installed smoothly. All the others had me googling for obscure forum posts. Sometimes I could "trick" cabal into installing the library but when I'd use it I'd get a runtime error related to a bad install. I've never ever ever had a problem like this with leiningen. But again, I'm experienced with maven.
On top of that, Cabal seems to install everything in a global namespace which scares me. If your Project1 needs LibraryX version 1 and Project2 needs LibraryX version 2 and these versions are incompatible, what do you do? Use something like cabal-dev I was told. But I have no idea what I'm doing with Cabal as it is. If something goes wrong with an abstraction on top of it, I'm totally screwed when it comes to debugging my problem.
I think I get why Haskell did this. Time is precious and if you need something like Curl, your OS has Curl and you don't want to make it your business to rewrite Curl in Haskell, you'd just reuse the OS library. But if I use a different OS, you kind of screwed me.
I love the Haskell language and miss all its lovely features. But if the language requires me to change my OS to develop anything useful, that's a nonstarter.
(from the README):
"occupation" =: "unemployed Haskell programmer"
"location" =: "mother's house"All in all, no, its package system is no RubyGems or what-have-you, but I think the (IMO) language makes it worth it.
HN may hate me forever for saying this, but after using Haskell, I could never imagine switching to a LISP. There's too many thumbnail clippings involved, and not enough types ;-).
Perhaps you may want to give https://github.com/organizations/Frege a try. It's a bit more powerfull than Haskell 2010, through support for higher rank types. OTOH, due to lack of supporters, the libraries available are far from complete. But it compiles to Java, and so is interoperable with Java or any other JVM language.
There's a curl binary for Windows that uses cygwin. I had no problems developing Windows haskell apps based off curl FFI.
I don't understand how this is a reasonable objection when http://prgmr.com/xen/ and https://www.virtualbox.org/ exist.
> Shen is a splendid compromise, but I found myself wishing it came with Haskell's uncompromising purity and Standard ML's straightforward type system.
I guess Haskell/ML minded people have a reason to think this way, but I'd be more interested to know whether someone is doing anything interesting with Shen. Have you got any "production" code written in it and running to show off?
EDIT: sorry! ...fixed the gender bug.
She's pretty fast to dismiss Shen.
This could also be a factor in the BODOL author's dismissal.
It's not like e.g. Python or Haskell have a fragmentation problem despite permissible licenses. Scheme has fragmentation problems but that seems to be a symptom of simple implementation and lack of a common set of libraries.
I think Shen is interesting, but if I used it I'd only be mining it for ideas to apply in other languages. It would make far more sense to me if he allowed derivatives iff they did not use the name.
It's the long winded, says the same things at least 5 (!) different times and ways, and therefore subject to many interpretations license, which is then subject to whims of the author or an external board (compare to Java, where in theory passing a suite of tests allows you to offer a version of Java(TM)).
I'm not going to invest serious effort in something that might get killed simply for political reasons including different interpretations of the license; even if I trusted the good will of everyone involved, sooner or later the players will change.
You could create a more functional version as long as it had a switch or default to the normal behavior and it was blessed by the powers that be (see comments on license), but you'd be "fighting the system" and all that.
I know it's a nitpick, but it seems strange to include characters that aren't on the keyboard as part of the default syntax.
> it seems strange to include characters that aren't on the keyboard as part of the default syntax
You say "not practical for anyone" and "seems strange", but maybe restricting a computer language to ASCII only is stange. With Pinyin IME's (e.g. Google's or Baidu's) people can easily and quickly enter thousands of characters not on the keyboard. It would be easy to someone to develop an IME to enable symbols to similarly be entered - (I mean a non-specific-IDE-based one, i.e. without having to go thru Eclipse or Emacs).
Also, avoiding to use nesting to introduce new identifiers goes a long way for clarity. I do hate "let" with a passion.
I find the parenthesis make me more aware of scope than other languages.
(define (naive-for start end my-body) (let ([operator (if (< start end) + -)]) (my-body) (cond [(not (eq? start end)) (naive-for (operator start 1) end my-body)])))
(naive-for 1 -2 (lambda () (displayln "For loop? Who needs those!")))
(define-syntax naive-for
(syntax-rules ()
[(naive-for start end body ...)
(let ([op (if (< start end) + -)])
(let loop ([s start] [e end])
(begin body ...)
(cond [(not (eq? s e))
(loop (op s 1) e)])))]))
(naive-for 1 -2
(displayln "Lambda? What lambda?"))Nitpicking aside, I fully approve of the goals and look forward to browsing the source.