Hacker News new | ask | show | jobs
by pacala 4290 days ago
Great comment, provides good food for thought.

The core FP idea is to focus on immutable data and data transformations. This is the minimal set of concepts one needs to juggle to get computations going. When modules communicate, they need to pass data and identify the transformations, so there is no dichotomy here between FP and OO (!). Especially if you think of method tables as data.

The String / Data.Text split in Haskell is an artefact of Haskell's ecosystem. It is not a conceptual hurdle, but rather an implementation detail. It is not too hard to imagine a different FP ecosystem where one can readily substitute different implementations under the same immutable data structure API, all with very explicit parametrization of the data transformations. All of (1)immutability, (2)simple data API, (3)polymorphism and (4)explicitism are important. Note that OO systems encourage (3), while FP systems encourage (1), (2) and (4).

Code as if you have immutable data and apply data transformations, tune performance by using the best implementations under the common simple data API. The question becomes how to build a system where all of them are ergonomic to use. IMHO, Haskell is not quite it, rather places like Dart / C# offer better ergonomics.

The other example is also thought provoking. In a system with polymorphism support, it's relatively straightforward to supply one's favorite String implementation, including one that prints a log on every String construction. The question is how to provide the new module to clients, which is reminiscent of dependency injection, but concrete implementations of DI are magic bad. In an explicit style, this would be realized by making modules functors of other modules and explicitly passing in the method tables:

  function Foobar(string) 
    return {
      foo: function(x) 
        return string.concat(x, string.new('abc')) 
      end
    }
  end

  function main1()
    string = String()
    foobar = Foobar(string)
    foobar.foo(string.new('xyz'))
  end

  function LoggingString()
    return String() + {
      new: function(x)
        print(x)
        return String.new(x)
      end
    }
  end

  function main2()
    string = LoggingString()
    foobar = Foobar(string)
    foobar.foo(string.new('xyz'))
  end
But it takes discipline to write the above and not sprinkle the code with String().new(...) everywhere, which defeats the purpose.
1 comments

How does FP, most notably Haskell, not encourage polymorphism? This statement is just plain wrong.

Type classes are the definition of polymorphism. And you can write very generic, abstract, polymorphic code using these constructs. It's not any languages fault if you write your code expecting only concrete types. By this standard it's Javas fault if the developer isn't using generics. No it's not it's the developers fault. The mechanisms are there. Use them.

I'm not hacking Haskell very often, but I get now and then vibe from the community that:

a. Avoid typeclasses until strictly necessary, http://www.reddit.com/r/haskell/comments/1j0awq/definitive_g...

b. Haskell has no first class instances. http://joyoftypes.blogspot.com/2012/02/haskell-supports-firs...

> The ability to have only a single instance for each class associated with a type makes little theoretical sense. Integers form a monoid under addition. Integers also form a monoid under multiplication. In fact, forming a monoid under two different operations is the crux of the definition of a semi-ring. So, why does it make any sense at all that in Haskell there can be at most one definition of “Monoid” for any type?

<removed useless stuff>