As part of a larger function definition, I needed to allow the domain (i, n) of a function to increment from i to n at varying rates. So I wrote:
f (i, n) k = [i, (i+k)..n]
into GHC. This returned odd results:
*Main> f (0.0, 1.0) 0.1
[0.0,0.1,0.2,0.30000000000000004,0.4000000000000001,0.5000000000000001,0.6000000000000001,0.7000000000000001,0.8,0.9,1.0]
Why does GHC return, e.g., 0.30000000000000004 instead of 0.3?
If i, n, and k are rational, you could go the infinite-precision route:
The notation may require a bit of getting used to:
Think of the
%as a funny looking fraction bar.You could view approximations with
The code above is written in an imperative style with full type annotations. Read its type as transforming a list of rational numbers into some I/O action. The
mapM_combinator fromControl.Monadevaluates an action (putRationalToOnePlaceLnin this case) for each value in a list (the rationals we want to approximate). You can think of it as sort of aforloop, and there is even aforM_combinator that’s identical tomapM_except the order of the arguments is reversed. The underscore at the end is a Haskell convention showing that it discards the results of running the actions, and note that there aremapMandforMthat do collect those results.To arrange for the output of the approximations via
putStrLn, we have to generate a string. If you were writing this in C, you’d have code along the lines ofThe Haskell code above is similar in structure. The type of Haskell’s
/operator isThis says for some instance
aof the typeclassFractional, when given two values of the same typea, you’ll get back another value of that type.We can ask
ghcito tell us aboutFractional:ghci> :info Fractional class (Num a) => Fractional a where (/) :: a -> a -> a recip :: a -> a fromRational :: Rational -> a -- Defined in GHC.Real instance Fractional Float -- Defined in GHC.Float instance Fractional Double -- Defined in GHC.FloatNotice the
instancelines at the bottom. This means we canor
but not
ghci> (22::Double) / (7::Float) <interactive>:1:16: Couldn't match expected type `Double' against inferred type `Float' In the second argument of `(/)', namely `(7 :: Float)' In the expression: (22 :: Double) / (7 :: Float) In the definition of `it': it = (22 :: Double) / (7 :: Float)and certainly not
ghci> (22::Integer) / (7::Integer) <interactive>:1:0: No instance for (Fractional Integer) arising from a use of `/' at :1:0-27 Possible fix: add an instance declaration for (Fractional Integer) In the expression: (22 :: Integer) / (7 :: Integer) In the definition of `it': it = (22 :: Integer) / (7 :: Integer)Remember that Haskell’s
Rationaltype is defined as a ratio ofIntegers, so you can think offromIntegralas sort of like a typecast in C.Even after reading A Gentle Introduction to Haskell: Numbers, you’ll still likely find Haskell to be frustratingly picky about mixing numeric types. It’s too easy for us, who perform infinite-precision arithmetic in our heads or on paper, to forget that computers have only finite precision and must deal in approximations. Type safety is a helpful reality check.
Sample output:
The definition of
printApproxprobably seemed comforting with all the helpful signposts such as names of functions and parameters or type annotations. As you grow more experienced and comfortable with Haskell, such imperative-looking definitions will begin to look cluttered and messy.Haskell is a functional language: its strength is specifying the what, not the how, by assembling simple functions into more complex ones. Someone once suggested that Haskell manipulates functions as powerfully as Perl manipulates strings.
In point-free style, the arguments disappear leaving the structure of the computation. Learning to read and this style does take practice, but you’ll find that it helps write cleaner code.
With tweaks to the imports, we can define a point-free equivalent such as
We see a few familiar bits: our new friend
mapM_,putStrLn,printf,numerator, anddenominator.There’s also some weird stuff. Haskell’s
$operator is another way to write function application. Its definition isIt may not seem terribly useful until you try
Prelude> show 1.0 / 2.0 <interactive>:1:0: No instance for (Fractional String) arising from a use of `/' at :1:0-13 Possible fix: add an instance declaration for (Fractional String) In the expression: show 1.0 / 2.0 In the definition of `it': it = show 1.0 / 2.0You could write that line as
or
So you can think of
$as another way to write parentheses.Then there’s
.that means function composition. Its definition iswhich we could also write as
As you can see, we apply the right-hand function and then feed the result to the left-hand function. You may remember definitions from mathematics textbooks such as
The name
.was chosen for its similarity in appearance to the raised dot.So with a chain of function compositions, it’s often easiest to understand it by reading back-to-front.
The
(numerator &&& denominator)bit uses a fan-out combinator fromControl.Arrow. For example:So it applies two functions to the same value and gives you back a tuple with the results. Remember we need to apply
fromIntegralto both the numerator and denominator, and that’s whatjoin (***) fromIntegraldoes. Note that***also comes from theControl.Arrowmodule.Finally, the
/operator takes separate arguments, not a tuple. Thinking imperatively, you might want to write something likewhere
but think functionally! What if we could somehow transform
/into a function that takes a tuple and uses its components as arguments for the division? That’s exactly whatuncurry (/)does!You’ve taken a great first step with Haskell. Enjoy the journey!