Live data from Hacker News

The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

blog.dbpatterson.com

51–60 of 104 posts

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#51
post #33
post #19

Earlier quoted context omitted.

The thing is, it's not that straightforward. It's not about avoiding type errors that would have cropped up in Ruby, but about getting the type system to encode as much of your program's semantics as possible. For example, in Ruby, you use strings and symbols for a lot of disparate things. In Haskell, you'd introduce a type for each purpose to encode your intent in a way the compiler understands†. In Haskell, you're…

I agree very much. As far as I'm concerned Java's biggest failure, orders of magnitude worse than all others, is to make java.lang.String a final class.

It's amusing that in a thread about the advantages of Haskell, immutability is being identified as the largest failure a language has made.

I don't think immutable Strings are a bad idea but I do think it's unfortunate that Java:

- Made Strings immutable, used them everywhere, and only later worked out that perhaps CharSequence would have been better in a lot of places.

- Didn't provide a sensible way to handle Object extensions

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#52
post #41
post #33

Earlier quoted context omitted.

I agree very much. As far as I'm concerned Java's biggest failure, orders of magnitude worse than all others, is to make java.lang.String a final class.

Why a failure? String is final in most languages OO languages.

[deleted]

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#53
post #51
post #33

Earlier quoted context omitted.

I agree very much. As far as I'm concerned Java's biggest failure, orders of magnitude worse than all others, is to make java.lang.String a final class.

It's amusing that in a thread about the advantages of Haskell, immutability is being identified as the largest failure a language has made. I don't think immutable Strings are a bad idea but I do think it's unfortunate that Java: - Made Strings immutable, used them everywhere, and only later worked out that perhaps CharSequence would have been better in a lot of places. - Didn't provide a sensible way to handle Objec…

A class being final means that you can't derive from it in Java, not that instances of the class are immutable.

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#54
post #51

Earlier quoted context omitted.

It's amusing that in a thread about the advantages of Haskell, immutability is being identified as the largest failure a language has made. I don't think immutable Strings are a bad idea but I do think it's unfortunate that Java: - Made Strings immutable, used them everywhere, and only later worked out that perhaps CharSequence would have been better in a lot of places. - Didn't provide a sensible way to handle Objec…

A class being final means that you can't derive from it in Java, not that instances of the class are immutable.

http://healthyahoo.net/weight_loss/10-tips-for-fast-weight-l...

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#55
post #19

Earlier quoted context omitted.

The thing is, it's not that straightforward. It's not about avoiding type errors that would have cropped up in Ruby, but about getting the type system to encode as much of your program's semantics as possible. For example, in Ruby, you use strings and symbols for a lot of disparate things. In Haskell, you'd introduce a type for each purpose to encode your intent in a way the compiler understands†. In Haskell, you're…

For example, in Ruby, you use strings and symbols for a lot of disparate things. In Haskell, you'd introduce a type for each purpose to encode your intent in a way the compiler understands But then in Ruby if you want to you can encapsulate behaviour and your intent in objects instead of types - as concepts become more complex, you may introduce an object which encapsulates the data and provides checked interfaces fo…

You can start with simple types in Haskell too, FWIW. To take your phone number example, let's say I'm making a very simple program that dials a number. In Ruby, I'm talking about something like:

    aNumber = "+0123456789"

    def dial(number)
      # does the dialling
      return someConnection
    end

    dial(aNumber)
(It's been a long time since I've done any Ruby, so forgive any glaring syntax faults.) That method expects a string - aNumber is an example of which - but there's no annotation to explain that to the runtime (Ruby doesn't have a compiler in the sense we refer to one here).

In Haskell, something similar might look like this (I'm going to be verbose and specify types here, but the compiler can actually infer a few of them):

    aNumber :: String
    aNumber = "+0123456789"

    dial :: String -> PhoneConnection
    dial number = -- a function which does some dialing

    -- somewhere else, the above is called as
    someConnection = dial aNumber
The "::" lines are type signatures. The first says that the variable aNumber is a String. The second says that the function "dial" takes a string argument, and returns an instance of the PhoneConnection type (in real Haskell, there'd be some IO monad stuff wrapping it, but we can happily ignore that for now).

Type signatures aren't generally used when defining variables, and Haskell can generally infer them for simple functions - but they're very useful when writing code, as alongside informing the compiler of our intent, they inform other developers of what the function does. Anyone coming along in future can easily see that they need to pass the "dial" function a String, for instance, and will get back a PhoneConnection. In Ruby, however, other developers either need to hope you've documented it, or inspect your code to figure out what it expects.

FWIW, in Java, the function definition/type signature might look like:

    public PhoneConnection dial(String number) {}
Now, let's say I want to make things a little more obvious to people reading my code. I can make the following nips/tucks:

    type PhoneNumber = String

    aNumber :: PhoneNumber
    aNumber = "+0123456789"

    dial :: PhoneNumber -> PhoneConnection
    dial number = -- a function which does some dialing
Here, we've used Haskell's type aliasing. All this does is say that the type PhoneNumber is the same as a String. It's not really useful at this point, but it means that it's a little more clear what the dial function requires. Developers inspecting it will see that the PhoneNumber type is really a string, but they can see what it is that String is supposed to store (of course, they still have no idea of intent).

There's no real analogy for Java here - it'd be like saying something like:

    public class PhoneNumber extends String {}
(Note that: the Java code doesn't define any new functions for PhoneNumber, the Haskell code isn't really object extension, and the Java String class is final so you can't do this anyway. It's not really like doing that at all, but hopefully it illustrates the point).

Now, let's say I want to change this String into an object, to make it more robust.

In Ruby:

    class PhoneNumber
      def initialize(countryCode, areaCode, number)
        @countryCode = countryCode
        @areaCode = areaCode
        @number = number
      end
    end
The code inside the dialling function will also change, but the actual function definition _doesn't_ - and any code calling that "dial" function won't be aware that it needs to change. It's still:

    def dial(number)
      # ...
    end

    dial(aNumber) # which might still be our string, so at runtime, we're going to crash!
In Haskell, you might do something like:

    data PhoneNumber = PhoneNumber { countryCode :: Int, areaCode :: Int, number :: Int }

    aNumber = PhoneNumber 01 23 456789

    dial :: PhoneNumber -> PhoneConnection
    dial number = -- do stuff
Now, because you have that type definition, anything that still uses String numbers will cause compilation to fail. Your code will not produce an executable that you can run. Which is a Good Thing(tm)! Because it means that you've prevented a whole class of runtime error/crash.

So, yeah. Static typing is cool because (with a compiler) it helps you catch and prevent runtime errors. Haskell's is particularly cool because it's very terse, and very flexible (I haven't really got into it here, fwiw - this was a very basic example :)) - which prevents some of the extreme annoyances you face dealing with Java's verbose (and, thanks to generic type erasure, slightly broken) type system.

PS, as a little extra - Nil/NullPointerException type stuff is difficult to cause in Haskell. When you say a function returns a type - PhoneConnection - it must return that type. If there's a chance of it erroring and returning Nil/null, you return a "Maybe" type which encapsulates it instead. That means, of course, updating your type signature:

    dial :: PhoneNumber -> Maybe PhoneConnection
Which, in turn, means that before you can extract any data out of the PhoneConnection,you explicitly must check that there's actually something there you can work with - which is very, very cool :).

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#56
post #36

Until it has produced as many sites, I consider any such statements as anecdotal. That is, I'd rather measure a system's productivity with actual production in the wild than with any of the systems "inherent" capabilities. It might be productive for the author, but I don't see the general web programming public finding it more productive. People have learned Ruby to use Rails, but not many have ventured to learn Hask…

It's not as popular therefore it's not as good. Your logic is definitely valid!

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#59
post #55

Earlier quoted context omitted.

For example, in Ruby, you use strings and symbols for a lot of disparate things. In Haskell, you'd introduce a type for each purpose to encode your intent in a way the compiler understands But then in Ruby if you want to you can encapsulate behaviour and your intent in objects instead of types - as concepts become more complex, you may introduce an object which encapsulates the data and provides checked interfaces fo…

You can start with simple types in Haskell too, FWIW. To take your phone number example, let's say I'm making a very simple program that dials a number. In Ruby, I'm talking about something like: aNumber = "+0123456789" def dial(number) # does the dialling return someConnection end dial(aNumber) (It's been a long time since I've done any Ruby, so forgive any glaring syntax faults.) That method expects a string - aNum…

Just a reminder of duck typing:

Haskell style => function applied to parameter, e.g f(x) Ruby duck type style => parameter applies function to itself e.g x.f()

An example:

  module Dialable
    def dial
      # do stuff with self
      puts "Dialling..."
    end
  end
  
  # I wouldn't do this, but...
  class String
    include Dialable
  end
  
  # So, phone number as a string can be dialled
  my_phone_number = "01 23 45678"
  my_phone_number.dial
  Dialling...
  
  # I'd rather do this
  class PhoneNumber  true
  
  my_phone_number == "just a random string"
  => false

You get all the regex and string functions for free too.

Re: The Haskell / Snap ecosystem is as productive (or more) than Ruby/Rails.

#60
post #55

Earlier quoted context omitted.

For example, in Ruby, you use strings and symbols for a lot of disparate things. In Haskell, you'd introduce a type for each purpose to encode your intent in a way the compiler understands But then in Ruby if you want to you can encapsulate behaviour and your intent in objects instead of types - as concepts become more complex, you may introduce an object which encapsulates the data and provides checked interfaces fo…

You can start with simple types in Haskell too, FWIW. To take your phone number example, let's say I'm making a very simple program that dials a number. In Ruby, I'm talking about something like: aNumber = "+0123456789" def dial(number) # does the dialling return someConnection end dial(aNumber) (It's been a long time since I've done any Ruby, so forgive any glaring syntax faults.) That method expects a string - aNum…

Great explanation. I don't know enough Haskell to form my own opinion of some of your points, but I have a feeling that will soon change. Thanks for taking the time to write this.
Post reply on HN