Hacker News new | ask | show | jobs
by david-given 4075 days ago
The author's comments on noise chime true with me: every time I give Haskell a try I end up struggling with frustrating and opaque vocabulary, sometimes completely at odds with the way other languages use them: e.g. C++ also has functors, and they're completely unrelated to Haskell functors.

I really like the author's suggestion of mentally translating Functor to Mappable. Are there any other synonyms for other Haskell terms of art?

What I'd really like, I suppose, is a complete overhaul of Haskell syntax to modernise and clarify everything: make it use actual words to describe things (foldl vs foldl'? BIG NO). Put in syntax redundancy and visual space to avoid the word soup effect: typing is cheap, understanding is expensive. Normalise and simplify terminology. Fix the semantic warts which make hacks like seq necessary --- if I need to worry about strictness and order of evaluation, then the language is doing lazy wrong. etc.

Basically I want language X such that X:Haskell like Java:K&R C.

This will never happen, of course; the people who have the knowledge to do such a thing won't do it because they are fully indoctrinated into the Haskell Way Of Life...

7 comments

As others pointed out, the way Haskell uses the term "functor" is related to the way mathematicians had been using it for at least a decade before cfront.

I agree that a shared vocabulary is important, but standardizing in a way that makes the mathematical writings on the topic more accessible seems a big win. Moreover, "functor" is a bit more precise than "mappable" - a functor is a mapping that preserves structure. In what sense? The math can guide you. In this case, it means the functor laws.

That's not to say that coming up with other associations to help ease understanding is a problem - I have no problem with saying, "for now, think of Functor as 'mappable'". The equivalent for Monad would probably be "flatMappable", and Monoid would be "appendable".

> As others pointed out, the way Haskell uses the term "functor" is related to the way mathematicians had been using it for at least a decade before cfront.

Rather a bit more than that. Eilenberg and Maclane's original paper defining the basic notions of category theory was published in 1945! http://www.ams.org/journals/tran/1945-058-00/S0002-9947-1945...

Thanks! I suspected that was the case, but the looser bound was much easier to be confident in with the level of effort I could spare.
For Monad, 'join' or the kleisli arrow (>=>) might give better intuition than bind/flatMap - maybe something like "contextComposable": kleisli arrow in the Identity monad is just function composition, and for everything else, it's function composition within a context, combining contexts according to 'join'.
I'm... hesitantly okay with "Functor == Mappable", but I really think Monad should be "Embedded DSL" and Monoid "mergeable". Or really Semigroup.

Monad is definitely abnormally difficult to humanize. The trio (T, ∀ a. a -> T a, ∀ a b. (a -> T b) -> (T a -> T b)) is really hard to nail down.

I like "embedded DSL" for Monad, although I think more specialized notions might provide more hooks to hang understanding on in particular cases.

I don't object to "mergeable" for Monoid, but I think I weakly prefer "appendable" since it seems to say a little more about how things merge (and of course the free monoid is exactly that).

Speaking again to the broader context, one thing I really like about Haskell's choice of naming these abstractions after the math is that this type of discussion has no bearing on what types adhere to the abstractions - we're not left arguing over whether Sum and Product are "really" appending, or set intersection is "really" merging. Integers are a clearly monoid under Sum and Product, and set intersection is clearly a semigroup but not a monoid (if our universe is open) because there is no identity.

Its interesting, I know one Cambridge theoretical physicist who is immensely successful in his field. I found it really odd how he worked 5 years as a proffesional writer between working in university. He thinks of good writing as his "secret weapon" that made him successful in academia. He is one of the most well spoken person I have met and has instilled new respect for being a good writer which I didn't think about before ( I like the HN crowd think programming is everything ).

Its also interesting that pg is such an accomplished writer. I think programmers need to think about code and well written document as having the same importance.

Just my two thoughts.

> e.g. C++ also has functors, and they're completely unrelated to Haskell functors.

You can't blame that one on Haskell or the functional community - the term was already established before the C++ community decided to use it in spite of pre-existing definitions. They even ignored Prolog's pre-exising abuse of the term functor :-)

A few similar terminological accidents of history come to mind, where the original definition of some term is now obscure and a different definition popular:

- POSIX capabilities (as implemented in e.g. Linux), which are a security mechanism that has nothing to do with what security researchers have been calling capabilities since the 1970s

- Microsoft operating systems using the term "Format" for creating a file system, despite the fact that it is impossible to actually format hard disks at the hardware level since the 1990s

- imperative programming languages abusing the term "function" to mean procedures with side effects

- "thunk" meaning a stub that emulates/bridges different calling conventions, instead of a call-by-name (or lazy) closure

- "Tea Party" used to refer to a fine rock band from Canada

Functors and monads are somewhat not going to change their names, partly because Haskell derives from math and those are what they're called over there.

But foldl' is horrible, I agree.

foldl' is a consistent and meaningful name. fold performs a fold without specifying an order[1], foldl folds from the left, and foldl' is a non-lazy version of of foldl.

1: fold :: (Foldable t,Monoid m) => t m -> m

Is there a spec that leaves the order unspecified? Having it not be a right fold would be quite nutty. It is a right fold in implementation.
Ugg, I had to spend a fair amount of time coming up with a good example. Hope this is helpful!

The Monoid operation mappend is guaranteed to be associative, so the order is irrelevant. Data structures can fold in whatever way is most efficient for their structure.

It's true that lists and arrays are implemented as right folds, however the fold implementation for sets is neither:

From Data.Set:

  fold = go
    where go Tip = mempty
          go (Bin 1 k _ _) = k
          go (Bin _ k l r) = go l `mappend` (k `mappend` go r)

  -- Here, I reorganized the code of `fold` to have the same shape as
  -- `foldl/foldr` so that you can see the difference in structure more
  -- clearly.
  fold2 = fold3 mappend mzero
  fold3 f z = go z
    where
      go z' Tip           = z'
      go z' (Bin _ x l r) = f (go f z' l) (f x (go f z' r))

  foldl f z = go z
    where
      go z' Tip           = z'
      go z' (Bin _ x l r) = go (f (go z' l) x) r

  foldr f z = go z
    where
      go z' Tip           = z'
      go z' (Bin _ x l r) = go (f x (go z' r)) l
I went through some examples to make 100% sure that fold has different behavior than foldl/foldr:

  fold      [[_ 4 _] 3 _] → f 4 (f 3 #)
  fold2 f # [[_ 4 _] 3 _] → f (f # (f 4 #)) (f 3 #)
  foldl f # [[_ 4 _] 3 _] → f (f # 4) 3
  foldr f # [[_ 4 _] 3 _] → f 3 (f 4 #)
Reductions:

  fold [[_ 4 _] 3 _]
  f    (go [_ 4 _])  (f 3 (go []))
  f    4             (f 3 (go []))
  f    4             (f 3 #)

  fold2 [[_ 4 _] 3 _]
  fold3 f                       #             [[_ 4 _] 3 _]
  f     (go [_ 4 _])            (f 3 (go _))
  f     (f (go _) (f 4 (go _))) (f 3 (go _))
  f     (f #      (f 4 #     )) (f 3 #     )
  f     (f #      (f 4 #     )) (f 3 #     )
  f (f # (f 4 #)) (f 3 #)

  foldl f                     #             [[_ 4 _] 3 _]
  go    #                     [[_ 4 _] 3 _]
  go    (f (go # [_ 4 _]) 3)  _
  f     (go # [_ 4 _])        3
  f     (go (f (go # _) 4) _) 3
  f     (go (f # 4) _)        3
  f     (f # 4)               3

  foldr f                    #                      [[_ 4 _] 3 _]
  go    #                    [[_ 4 _] 3 _]
  go    (f 3 (go # [_ 4 _])) _
  f     3                    (go # [_ 4 _])
  f     3                    (go (f 4 (go # _)) _)
  f     3                    (f 4 (go # _))
  f     3                    (f 4 #)
It's useful for more general folds over more general types. If you have a tree structure then you might not want to fold from the left or the right but instead in multiple places in parallel and then combine them at the end

    * + (* + (* + (* + (* + (* + *)))))   versus
    (((((* + *) + *) + *) + *) + *) + *   versus
    ((* + *) + *) + (* + (* + *))
Oh God, right. The changes over the years have left me bamboozled. Is (.) fmap yet?
I mentally pronounce foldl' as "fold ell prime," and it never gives me any difficulty.
> sometimes completely at odds with the way other languages use them: e.g. C++ also has functors, and they're completely unrelated to Haskell functors.

Really, this should be considered as C++ perverting the existing terminology from category-theory for Functors.

> I really like the author's suggestion of mentally translating Functor to Mappable. Are there any other synonyms for other Haskell terms of art?

I think that there is a great deal to be said for leveraging intuition. But who's intuition? Who was Haskell designed by/for when Functor was first defined in the standard library?

> What I'd really like, I suppose, is a complete overhaul of Haskell syntax to modernise and clarify everything: make it use actual words to describe things (foldl vs foldl'? BIG NO).

The intention is admirable, but what does it cost to do it, and what is gained by doing it? It seems that the implication is that certain functions become immediately intuitive to people (what kind of people?) in certain contexts, and that possibly-by-analogy, these context can be extended (how far?). I'm not saying that this is a bad goal, but rather than try to compromise in this manner, the Haskell community has often adopted terminology that is precise instead of intuitive.

Functors could have been Mappables, but how far would that analogy hold, and who is already familiar with maps in this context? Better to use an accurate term, and when someone unfamiliar with it learns it in this context, they will be able to apply it to many other contexts.

> Put in syntax redundancy and visual space to avoid the word soup effect: typing is cheap, understanding is expensive. Normalise and simplify terminology.

On the surface, I've always supported this - if only for the reason that I would always like to be able to pronounce a combinator when I'm talking to someone. The downside would be the combinatorial explosion of different subsets of names that people would learn for even one library. I'm not sure weather it would be a net plus or minus.

> Fix the semantic warts which make hacks like `seq` necessary --- if I need to worry about strictness and order of evaluation, then the language is doing lazy wrong. etc.

I think you will find that this is an unsolved problem. Better to allow people to be explicit when necessary instead of making the language totally unusable.

> Basically I want language X such that X:Haskell like Java:K&R C.

I think I understand the sentiment, but the analogy feels too shallow. For instance, I would make the following predictions from your analogy - Do they hold?

* Runs on a virtual machine instead of being compiled * Extraordinary measures taken to make the language and binary-formats backwards compatible. * More type-safe * Less primitives * More automated memory-management

> This will never happen, of course; the people who have the knowledge to do such a thing won't do it because they are fully indoctrinated into the Haskell Way Of Life...

Indoctrinated is obviously a loaded term. I think you will find that nearly all Haskell programmers in any position to influence the development of the language are very open-minded when it comes to new ideas. Part of the reason why Haskell looks the way it does today is because it was intended to be a platform for experimentation.

> > sometimes completely at odds with the way other languages use them: e.g. C++ also has functors, and they're completely unrelated to Haskell functors.

> Really, this should be considered as C++ perverting the existing terminology from category-theory for Functors.

Only if you assume that category theory is the correct source of meaning of such terminology. But Wikipedia, for example, lists functor as being ambiguous - there's the category theory version, and there's the programming version, which is a function object. It lists a bunch of languages (C++, C#, D, Eiffel, Java, JavaScript, Scheme, Lisp, ObjectiveC, Perl, PHP, PowerShell, Python, and Ruby) that support some variant on this theme. It seems rather arrogant to say that the category theory definition is the one that should be the one we mean when discussing a programming language, rather than the one used by a large number of programming languages.

The definition of functor in category theory is from 1945 while most of today's mainstream programming languages are much younger than that. The people who wrote those definitions for C++, C#... simply ignored the existing terminology.
Fair enough. And yet, at the time (and even today, outside of the FP crowd), category theory is really far outside the scope of what most people consider programming, so it's hard to blame them for not looking there for terms.

For that matter, category theory borrowed the word from linguistics, and most definitely did not keep the same meaning.

I certainly wouldn't blame anyone for not being familiar with the terms. What I would blame people for is attacking Haskell's choice of terminology due to it not 'being like c++'. A generous interpretation of intent goes a long way, and if that is out of reach, at least do enough research to make sure you're not pointing out weaknesses based on a false premiss. Instead I commonly see people starting from "I don't find Haskell intuitive" and extrapolating to "Haskell's contributors are indoctrinated and people who defend its use of established terminology are arrogant". Maybe I should just ignore such points of view but they seem to be infectious.
Well, let me see if I can meet you partway. Haskell is grounded in abstract algebra and category theory; using words like functor to mean what they mean in category theory is therefore a reasonable choice for Haskell.

But Haskell is a programming language. Using words like functor to mean something different from what other programming languages mean by the term creates a barrier to understanding for (non-FP) programmers. (The other definition is rather well established in non-FP circles, which is by far the majority of programming.) And when Haskell proponents state that their definition is right because it's the one from category theory, non-FP programmers find that rather arrogant.

you might like elm [http://elm-lang.org/]; it's inspired heavily by haskell, but the author thinks long and hard about finding the right names for things rather than just using the defaults from haskell or ml.
actually, Scala is in some ways similar in that it has higher kinded types allowing you to abstract over functors/monads/traversables

for expressions in scala are monadic comprehension and implicit parameters are analogous to typeclass constraints.