Live data from Hacker News

How it feels to join an all-Haskell startup

wagonhq.com

11–20 of 59 posts

Re: How it feels to join an all-Haskell startup

#11
post #8

Could someone explain 'applicative'? To me it seems like throwing some magic syntax at a simple usecase to make it more terse and less readable - but maybe I misunderstand and it's something every Haskell programmer knows.

In most cases (like the example), it simply allows you to call "effectful" code (often monadic, IO etc) to populate arguments of a function. My favorite example is "I want to grab three web pages concurrently and return them in a single triple." To the haters who think this is just golf, just TRY to write this as beautifully in any other language. (page1, page2, page3) Concurrently (getURL "url1") Concurrently (getUR…

Alice ML:

    val (page1, page2, page3) = (spawn getURL "url1",
                                 spawn getURL "url2",
                                 spawn getURL "url3")
The requests are concurrent. Spawn returns a future and 'page1', etc are futures immediately. When the value of 'page1', etc is requested later the requesting process either blocks until the getURL for that value is complete, or transforms into the value implicitly if it's already done.

I think it's just as nice as the Haskell example. I do agree that Haskell is a great language though.

Re: How it feels to join an all-Haskell startup

#12

How was that snippet an improvement to your coding style? The original was extremely clear, and now someone who reads your code has to understand what (,) and do. IMO this tendency for code golf one-upmanship is a huge detractor for anyone wanting to learn the language.

This is common complaint I see of the Haskell community where there is always an attempt at cleverness over clarity.

Unfortunately I think it's the language, not the community. It definitely happenes with my own code as well, no hints from external developers are required :-) I miss Python in this regard.

Re: How it feels to join an all-Haskell startup

#13
post #11
post #8

Earlier quoted context omitted.

In most cases (like the example), it simply allows you to call "effectful" code (often monadic, IO etc) to populate arguments of a function. My favorite example is "I want to grab three web pages concurrently and return them in a single triple." To the haters who think this is just golf, just TRY to write this as beautifully in any other language. (page1, page2, page3) Concurrently (getURL "url1") Concurrently (getUR…

Alice ML: val (page1, page2, page3) = (spawn getURL "url1", spawn getURL "url2", spawn getURL "url3") The requests are concurrent. Spawn returns a future and 'page1', etc are futures immediately. When the value of 'page1', etc is requested later the requesting process either blocks until the getURL for that value is complete, or transforms into the value implicitly if it's already done. I think it's just as nice as t…

I don't know Alice ML but one advantage of using abstractions like Applicative is that you can write code which is polymorphic in the particular applicative you choose. So, you can mock your concurrent requests using the Identity or ZipList applicative, and use the type class laws to prove things which are true about both. I'm guessing, but it looks like the spawn syntax is built-in here. Not that it's not as elegant as the Concurrent version, but likely not as powerful from an abstraction point of view.

Re: How it feels to join an all-Haskell startup

#14
post #11
post #8

Earlier quoted context omitted.

In most cases (like the example), it simply allows you to call "effectful" code (often monadic, IO etc) to populate arguments of a function. My favorite example is "I want to grab three web pages concurrently and return them in a single triple." To the haters who think this is just golf, just TRY to write this as beautifully in any other language. (page1, page2, page3) Concurrently (getURL "url1") Concurrently (getUR…

Alice ML: val (page1, page2, page3) = (spawn getURL "url1", spawn getURL "url2", spawn getURL "url3") The requests are concurrent. Spawn returns a future and 'page1', etc are futures immediately. When the value of 'page1', etc is requested later the requesting process either blocks until the getURL for that value is complete, or transforms into the value implicitly if it's already done. I think it's just as nice as t…

[deleted]

Re: How it feels to join an all-Haskell startup

#15

How was that snippet an improvement to your coding style? The original was extremely clear, and now someone who reads your code has to understand what (,) and do. IMO this tendency for code golf one-upmanship is a huge detractor for anyone wanting to learn the language.

The change they made was from a monad to an applicative, which is less "hands on", thus preferred. Kind of preferring map over hand-written loops. (Although the golfing is definitively there and in the community, see pointfree/pointless style https://wiki.haskell.org/Pointfree)

Re: How it feels to join an all-Haskell startup

#16
post #11

Earlier quoted context omitted.

Alice ML: val (page1, page2, page3) = (spawn getURL "url1", spawn getURL "url2", spawn getURL "url3") The requests are concurrent. Spawn returns a future and 'page1', etc are futures immediately. When the value of 'page1', etc is requested later the requesting process either blocks until the getURL for that value is complete, or transforms into the value implicitly if it's already done. I think it's just as nice as t…

I don't know Alice ML but one advantage of using abstractions like Applicative is that you can write code which is polymorphic in the particular applicative you choose. So, you can mock your concurrent requests using the Identity or ZipList applicative, and use the type class laws to prove things which are true about both. I'm guessing, but it looks like the spawn syntax is built-in here. Not that it's not as elegant…

Correct, spawn is built in in Alice ML. Haskell wins on all the ways it can be used beyond built in functionality compared to it.

Re: How it feels to join an all-Haskell startup

#17

How was that snippet an improvement to your coding style? The original was extremely clear, and now someone who reads your code has to understand what (,) and do. IMO this tendency for code golf one-upmanship is a huge detractor for anyone wanting to learn the language.

It's somewhat debatable whether the refactor is an improvement, but there is at least one good argument that it is, and (in my opinion) only a much poorer argument that it makes things less clear.

If the applicative code is better, it's because applicatives are strictly less powerful than monads, in the sense that everything you can do with applicatives you can also do with monads, but not vice-versa.

Many Haskellers prefer the abstraction that is "just powerful enough", or what's the same thing, they prefer the abstraction that is "least powerful"—that is, Haskellers try to write code in a way that lets them do what they want want to do, but at the same time in a way that doesn't allow them do things they didn't mean to do. And for just that reason: using the least powerful abstraction prevents you from introducing bugs by keeping you from writing some of the things that you didn't intend to write.

Thus the least powerful abstraction is the best abstraction, in the sense that it's the "best fit" (and, yes, also in an aesthetic sense). So if you can write what you want using applicatives rather than monads, you should prefer applicatives, the argument goes. (And if you can write it with functors, prefer functors to applicatives.) For the same reason, many Haskellers prefer to use restrictions of the IO type that limit what you can do rather than IO itself, whenever it's convenient.

Applicative style, in this case, also keeps you from having to name a couple of things, which means two fewer "hard things" (in Dijkstra's estimation) for the programmer to deal with—and just two fewer things, which means two fewer things to screw up, period.

The counter-argument, that applicatives are somehow more complicated or less clear than monads, is a poor one, I think. Haskell is not a superficial language. It's not one you're meant to be able to read and understand just because you're a competent programmer in some other, unrelated language. If you want to program in Haskell, you need to understand the languange's core abstractions—and, yes, applicative style is one of them at this point. That does mean there's more to learn. But Haskell is all about giving programmers the ability to recognize and use the right abstraction; the time spent learning is worth your while.

Further, if you're not a Haskell programmer, I think you should beware of pretending that you understand the monadic "before" code and therefore thinking that it's simpler than the applicative "after" code that you don't pretend to understand. First, because (syntactic subterfuge notwithstanding) you don't really understand the monadic code, unless you've learned the underlying concepts; and second, because it's not simpler. It's just not. There is more going on in the monadic code, not less. Now, if you're a beginner and you understand monads but not applicatives, you have an argument—the monadic code is familiar, and the applicative code isn't—but that just means you have more to learn. Go learn it. We're here to help.

Re: How it feels to join an all-Haskell startup

#18

Could someone explain 'applicative'? To me it seems like throwing some magic syntax at a simple usecase to make it more terse and less readable - but maybe I misunderstand and it's something every Haskell programmer knows.

Applicatives are definitely something that every Haskell programmer knows. I would expect everyone on the team to understand the recommended new version, but it's less clear whether it is actually an improvement. There is a proposal to allow applicatives to use the same syntax as monads (the original code, before the change), which would render the question moot: https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo

Re: How it feels to join an all-Haskell startup

#19

How was that snippet an improvement to your coding style? The original was extremely clear, and now someone who reads your code has to understand what (,) and do. IMO this tendency for code golf one-upmanship is a huge detractor for anyone wanting to learn the language.

Of the three, I'd contend is the most reasonable to do since it's very similar to $ (which is commonly used), but applied to a boxed argument (Functor).

Re: How it feels to join an all-Haskell startup

#20
post #10

How was that snippet an improvement to your coding style? The original was extremely clear, and now someone who reads your code has to understand what (,) and do. IMO this tendency for code golf one-upmanship is a huge detractor for anyone wanting to learn the language.

I was about to try to talk about the genuine elegance of Applicatives—and I'm more than happy to defend them in general—but then, yeah. In this instance, I'm not sure I'd be able to argue that the change was such a good choice. liftIO (liftA2 (,) randomIO getCurrentTime) Eh, I'm not sure I could defend it. I'd honestly probably actually do bid

Of course, there is always https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo
Post reply on HN