News for hackers. Stuff that matters.
Re:Go 2, here we come by jolmg on Thursday November 29, 2018 @06:37PM (#18565234)
I like how Haskell does it. When you simply write a number like "3" it will infer the type to be "Num a => a" which means it could be any type you have loaded that defines the functions in the typeclass Num:

    class Num a where
      (+) :: a -> a -> a
      (-) :: a -> a -> a
      (*) :: a -> a -> a
      negate :: a -> a
      abs :: a -> a
      signum :: a -> a
      fromInteger :: Integer -> a
"3.0" would be "Fractional a => a" which means it defines the functions in the typeclass Fractional (in addition to being a Num):

    class Num a => Fractional a where
      (/) :: a -> a -> a
      recip :: a -> a
      fromRational :: Rational -> a
Depending on how you use the value, the type would be further refined in compile-time. For example, if you did `recip 3`, 3 couldn't be any Num anymore, it would have to be some Fractional.

Regarding (5/3)*6, it does equal 10 using floating point. A better example would be how 0.1 + 0.2 is not equal to 0.3. We can see this:

    ghci> 0.1 + 0.2 == (0.3 :: Float)
    False
If we specified that we're working with Rational values, which are also Fractional a => a, and which are defined as a pair of integers, one representing a numerator and the other a denominator, roughly like so:

    type Rational = Ratio Integer
    data Ratio a = a :% a
then we can see that 0.1 + 0.2 does equal to 0.3:

    ghci> 0.1 + 0.2 == (0.3 :: Rational)
    True
It's pretty cool that Haskell lets you define new types of numbers and use them like any other. While you can transparently support hardware type numbers, represented by types like Int, Float, Double, Word, Word8, Word16, Word32, Word64, you also have transparent support for arbitrary precision Integer and Rational. Writing your functions to work with the typeclasses like Num and Fractional lets your functions work with any of these types and future types defined.
Re:Go 2, here we come by joppy on Thursday November 29, 2018 @10:09PM (#18566319)
This is a great system for tying in new number types with existing ones, but the lack of explicitness about casting exact types (Integer, Rational) to inexact types (Int, Float) has led to many bugs and confusions for me. Coupled with the fact that so many standard functions (like length, for example) want to return an Int rather than an Integer, it just leads to me spewing ((fromIntegral ___)::Integer) all over my code.

I love all of the automatic casting between exact types, but happily implicitly casting to inexact types is (in my opinion) a big mistake.

Re:Go 2, here we come by runeks on Sunday December 02, 2018 @11:34AM (#18582982)
> [...] but the lack of explicitness about casting exact types (Integer, Rational) to inexact types (Int, Float) has led to many bugs and confusions for me.

What do you mean? As far as I'm aware, you always have to explicitly convert exact types to inexact types in Haskell (using "fromIntegral" and "realToFrac").

> Coupled with the fact that so many standard functions (like length, for example) want to return an Int rather than an Integer, it just leads to me spewing ((fromIntegral ___)::Integer) all over my code.

Indeed this is rather annoying. It's this way because of legacy reasons (see https://www.reddit.com/r/haskell/comments/60u10b/do_function...).

Re:Go 2, here we come by trobertson on Thursday November 29, 2018 @10:23PM (#18566372)
If you don't like the automatic cast, you can always manually cast inline by doing (3 :: Int). I've used this a number of times when doing numeric stuff in Haskell.
Re:Go 2, here we come by joppy on Thursday November 29, 2018 @10:37PM (#18566450)
I do know how to manually cast, although in my case I'm usually casting to Integer. I was arguing that a cast from Num to Float, Double, Int etc is a lossy operation, and breaks the semantics of arithmetic, and so should not be done implicitly.
Re:Go 2, here we come by jolmg on Thursday November 29, 2018 @11:04PM (#18566568)
> but the lack of explicitness about casting exact types (Integer, Rational) to inexact types (Int, Float) has led to many bugs and confusions for me.

> I love all of the automatic casting between exact types, but happily implicitly casting to inexact types is (in my opinion) a big mistake.

> I was arguing that a cast from Num to Float, Double, Int etc is a lossy operation, and breaks the semantics of arithmetic, and so should not be done implicitly

It's not really casting; it's just type inference. You can't have a Num and use it as a Rational in one place and a Float in another (unless you lift the monomorphism restriction), and you can't add a Rational and a Float without explicitly casting one to the other by the use of a function like fromRational. Like so, real casting is very much explicit. I feel like you're technically arguing against representing Floats with decimal numbers in code, but I don't think you really mean that.

EDIT: At the risk of stating something that you might already know, (x :: Int) in Haskell is not a cast like (int)x in C. (x :: Int) simply further restricts the list of possible concrete types that x could be. If the code context implies that x is a Num a => a, that means it could one of an Int, a Float, etc. Doing (x :: Int) simply says to restrict those possibilities to only Int. If x were a Float, and you did x :: Int, that'd be a type error because it was never possible for it to be an Int, only a Float. You'd need to cast by using a function like floor, ceiling, or round.

EDIT 2: To further explain this, C's (int)x is a run-time operation that converts whatever x is to an int, while Haskell's x :: Int is a compile-time operation that states that x can only ever be an Int and never something else. At the end of compiling, Haskell needs everything to be concrete types. Num a => a is not concrete because it can be many types and if Haskell can't resolve it to a single concrete type implicitly by context or explicitly by signatures like x :: Int, then it has to resort to defaulting rules or raise a compile-time error informing the programmer of the type ambiguity.

EDIT 3: On:

> Coupled with the fact that so many standard functions (like length, for example) want to return an Int rather than an Integer, it just leads to me spewing ((fromIntegral ___)::Integer) all over my code.

I agree it would be nice for length to return a Num a => a instead of an Int, but it's nice that the reason is to maintain stability and limit code breakage from the times before Num existed. The Haskell community seems to really care about that. They did add genericLength which does return a Num a => a, though. There's other generic functions added to Data.List.