7 ms·
It's different. It's like you were listening to one music genre all your life and there's suddenly something new. If, say, Python was the only language with "f
by sold 14y ago
It's different. It's like you were listening to one music genre all your life and there's suddenly something new.
If, say, Python was the only language with "for(each)" loops, you'd see many blog posts about that too. After I saw this, the old style of iterating by index feels so antiquated. Haskell gives the same feeling many times. It has unique features when it comes to abstraction and I feel they are the right way to program.
- rtpg 14y agostrangely enough, I think one of the interesting features (type classes) only ended up reappearing in Go with its interfaces (granted, only in a very limited fashion).
- DanWaterworth 14y agoGo's interfaces are not like Haskell's typeclasses. Just try and write a Go interface that allows you to add two of the same things together.
- codygman 14y agoWhat do you mean add two of the same things together exactly? If I'm not mistaken, what you are talking about is possible but I don't totally understand what you are saying. Could you provide an example?
- btilly 14y agoHe's talking about the inability to specify an interface that specifies functions that take two arguments of the same type. Go can't do that. You can sometimes work around it. For instance consider sorting. The natural approach is to specify a type that allows comparisons. Go can't do that. But in go you can have a collection which has a function Less(i, j int) bool that takes in two integers and compares the objects at those positions (which in turn just happen to be of the same type). But this is a limited work around, and there are plenty of cases where you can want something more flexible.
- danieldk 14y agoThe types of polymorphism are different. Haskell's parametric polymorphism is compile-time polymorphism, Go's interfaces are run-time polymorphism. If we draw a parallel to C++, Haskell's polymorphism is like templates, Go's interfaces are like abstract base classes (except that they use compile-time duck typing). For example, consider the (+) function in Haskell: (+) :: Num n -> n -> n -> n If you use an Integer as the first argument, the second argument must also be an integer, as well as the return type. You can observe this by currying, binding only the first argument: Prelude> :type (+) (1 :: Integer) (+) (1 :: Integer) :: Integer -> Integer Prelude> :type (+) (1 :: Double) (+) (1 :: Double) :: Double -> Double In Go, you could write an interface Num and define a function: func foo(a Num, b Num) Num However, if float64 and int both implement the Num interface, you could also foo a float64 and int. And it would return something that conforms to the Num interface, but what type is actually constructed depends on the implementation of foo. In other words, type classes have relatively little to do with Go interfaces, aside that they both implement a (different) form of polymorphism. It's possible to make something akin to Go's polymorphism in Haskell, but you'd need to hide the type parameter of the typeclass using existential quantification. data NumBox = forall n. Num n => MkNumBox n You can now use the NumBox for runtime polymorphism: Prelude> :type [1::Int, 4.4::Double] [...] Couldn't match expected type `Int' with actual type `Double' [...] Prelude> :type [MkNumBox (32 :: Int), MkNumBox (22.32 :: Double)] [MkNumBox (32 :: Int), MkNumBox (22.32 :: Double)] :: [NumBox]
- kevinnk 14y ago> If we draw a parallel to C++, Haskell's polymorphism is like templates It's important to note that even if Haskell's classes feel like C++ templates they are actually implemented more like abstract classes. In C++ you essentially create a new function for each template instantiation. In Haskell the function is passed an additional pointer telling the function how to implement the class. Kind of like a vtable pointer except not bound to the actual object. This has performance implications, so it's an important point to remember when comparing the languages.
- DanWaterworth 14y agoThat's a good point, however, the important aspect of typeclasses is that they're type-checked, not that they're compiled via monomorphization. Also, I believe that the way that typeclasses are compiled is at the discretion of the compiler, so actually typeclasses may be compiled in the same way as C++ templates. Another point is that monomorphisation isn't always possible, because a function could be run with an infinite number of types.
- tikhonj 14y agoI think one of the most important features of typeclasses is the ability to be polymorphic on just the return type of an expression. For example, there is a typeclass called Read which comes with a function called read: read :: Read r => String -> r That is, you get a function from a String to whatever type is in the typeclass. It's the opposite of toString. This is also used in a whole bunch of other contexts like numbers--numeric literals are polymorphic, letting you add any numeric type you want and still use literals for it. As far as I know, Go cannot do anything of the sort. There are some other nice features of typeclasses, like the ability to have multiple types. That is, you could have a typeclass for multiplication that allowed you to multiply two numbers, two matrices or a number with a matrix. You can even have typeclasses that are recursive, allowing you to define them for an infinite amount of types: you could have a typeclass for functions of any number of arguments, for example. I think Go interfaces do none of that as well. Now Rust, on the other hand, actually* has typeclasses.
- SilasX 14y agoI'm personally on a quest to "get" Haskell right now, but I really don't see what's so special about that or unique to Haskell. Languages have long accomplished what `read` does by simply casting/coercing the string into the desired type. In python, for example, I'd call int() on the input strings I want to turn into integers.
- tikhonj 14y agoThe point is that you use a single function for any type. So in Python you'd have to do int() or float() or customType()... In Haskell, all of these would be just `read'. The type system can figure out which instance to use for you, without having to specify it. Moreover, it's also trivial to write a function that works on any readable type, something hard (although not entirely impossible) to do in Python. This makes it much easier to use: whenever you want to get any type from a string, you just read it. This is just like being able to print values of any type, except for parsing. This can also be used with constants rather than functions. So maxBound is the maximum value for any bounded type. In Python, the closest you can get to that is something like float.maxBound. (Except, apparently, it's actually sys.float_info.max.) As I mentioned, this also lets you define new numeric types that can still use the same literals. For my most recent project, I needed 18-bit words. I could do this and still write expressions like `x + 1` using the Word18 type. Moreover, it would be very easy to make my code generic over the exact type of number used--this would make it possible to use numbers of different sizes or even something more exotic like random variables. (It happens to be tricky because some of the semantics I was working with rely on having exactly 18 bits, but that's an issue with the domain and not with Haskell.) In another language, I would either have to use the normal int type and make sure to always keep track of the overflow myself or I would have to wrap every literal in a function that turned into an 18-bit word. So the special quality is being able to dispatch on the return type of an expression rather than on the types of the arguments. I think this is very special indeed and extremely useful. I hope this clarifies everything.
- graue 14y agoRust has them too, it's described in this very interesting blog post that I haven't had time to fully read yet: http://smallcultfollowing.com/babysteps/blog/2012/04/09/rusts-object-system/ http://smallcultfollowing.com/babysteps/blog/2012/04/09/rust... "We also manage to combine traditional class-oriented OOP with Haskell’s type classes in a way that feels seamless to me."