Reputation: 179
I've been studying Haskell, since my teacher forced us to. As an exercise, we'll try writing up a solution to approximate sin(x) function using its Maclaurin series only up to the 30th term.
I thought up of using lists for the alternating series of 1 and -1, the x raised to an odd number, and the factorial of odd numbers. Then, I'll multiply the first two lists and divide the last. Until now I only have written this:
oddl = [1,3..]
powerl x = map (x^) oddl
factorial 0 = 1
factorial x = factorial (x - 1) * x
factl = map factorial oddl
alterl = scanl (*) (-1) [-1,-1..]
sined x = zipWith (*) (powerl x) alterl
sinex x = zipWith (/) sined factl
sinf x = foldl (+) (take 30 (sinex x))
Then in ghci
, I'll enter sinf 3.14
but before I can enter it after loading I get this:
exert.hs:31:8:
No instance for (Enum t0)
arising from the arithmetic sequence ‘1, 3 .. ’
The type variable ‘t0’ is ambiguous
Relevant bindings include oddl :: [t0] (bound at exert.hs:31:1)
Note: there are several potential instances:
instance forall (k :: BOX) (s :: k). Enum (Data.Proxy.Proxy s)
-- Defined in ‘Data.Proxy’
instance Integral a => Enum (GHC.Real.Ratio a)
-- Defined in ‘GHC.Real’
instance Enum Ordering -- Defined in ‘GHC.Enum’
...plus 8 others
In the expression: [1, 3 .. ]
In an equation for ‘oddl’: oddl = [1, 3 .. ]
exert.hs:31:9:
No instance for (Num t0) arising from the literal ‘1’
The type variable ‘t0’ is ambiguous
Relevant bindings include oddl :: [t0] (bound at exert.hs:31:1)
Note: there are several potential instances:
instance Integral a => Num (GHC.Real.Ratio a)
-- Defined in ‘GHC.Real’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
...plus three others
In the expression: 1
In the expression: [1, 3 .. ]
In an equation for ‘oddl’: oddl = [1, 3 .. ]
exert.hs:32:18:
Could not deduce (Integral t0) arising from a use of ‘^’
from the context (Num b)
bound by the inferred type of powerl :: Num b => b -> [b]
at exert.hs:32:1-24
The type variable ‘t0’ is ambiguous
Note: there are several potential instances:
instance Integral Integer -- Defined in ‘GHC.Real’
instance Integral Int -- Defined in ‘GHC.Real’
instance Integral Word -- Defined in ‘GHC.Real’
In the first argument of ‘map’, namely ‘(x ^)’
In the expression: map (x ^) oddl
In an equation for ‘powerl’: powerl x = map (x ^) oddl
exert.hs:35:13:
No instance for (Eq t0) arising from a use of ‘factorial’
The type variable ‘t0’ is ambiguous
Relevant bindings include factl :: [t0] (bound at exert.hs:35:1)
Note: there are several potential instances:
instance (Eq a, Eq b) => Eq (Either a b)
-- Defined in ‘Data.Either’
instance forall (k :: BOX) (s :: k). Eq (Data.Proxy.Proxy s)
-- Defined in ‘Data.Proxy’
instance (GHC.Arr.Ix i, Eq e) => Eq (GHC.Arr.Array i e)
-- Defined in ‘GHC.Arr’
...plus 28 others
In the first argument of ‘map’, namely ‘factorial’
In the expression: map factorial oddl
In an equation for ‘factl’: factl = map factorial oddl
exert.hs:38:23:
Couldn't match expected type ‘[t0]’
with actual type ‘Integer -> [Integer]’
Relevant bindings include
sinex :: t -> [t0] (bound at exert.hs:38:1)
Probable cause: ‘sined’ is applied to too few arguments
In the second argument of ‘zipWith’, namely ‘sined’
In the expression: zipWith (/) sined factl
exert.hs:39:16:
Could not deduce (Num [t0]) arising from a use of ‘+’
from the context (Foldable t)
bound by the inferred type of
sinf :: Foldable t => t1 -> t [t0] -> [t0]
at exert.hs:39:1-38
The type variable ‘t0’ is ambiguous
Relevant bindings include
sinf :: t1 -> t [t0] -> [t0] (bound at exert.hs:39:1)
In the first argument of ‘foldl’, namely ‘(+)’
In the expression: foldl (+) (take 30 (sinex x))
In an equation for ‘sinf’: sinf x = foldl (+) (take 30 (sinex x))
Failed, modules loaded: none.
so much messages. wow. I don't have any clue on what I have been doing wrong nor even know where to start reading the message. May anyone shed light on this? Actually as a beginner, I am curious on how is it wrong in Haskell and what's preventing it to work.
EDIT: After adding type signatures and a missing argument.
oddl :: [Integer]
oddl = [1,3..]
powerl :: Integer -> [Integer]
powerl x = map (x^) oddl
factorial 0 = 1
factorial x = factorial (x - 1) * x
factl :: [Integer]
factl = map factorial oddl
alterl = scanl (*) (-1) [-1,-1..]
sined x = zipWith (*) (powerl x) alterl
sinex x = zipWith (/) (sined x) factl
sinf x = foldl (+) (take 30 (sinex x))
Now I get a shorter error:
exert.hs:41:19:
No instance for (Fractional Integer) arising from a use of ‘/’
In the first argument of ‘zipWith’, namely ‘(/)’
In the expression: zipWith (/) (sined x) factl
In an equation for ‘sinex’: sinex x = zipWith (/) (sined x) factl
exert.hs:42:16:
Could not deduce (Num [Integer]) arising from a use of ‘+’
from the context (Foldable t)
bound by the inferred type of
sinf :: Foldable t => Integer -> t [Integer] -> [Integer]
at exert.hs:42:1-38
In the first argument of ‘foldl’, namely ‘(+)’
In the expression: foldl (+) (take 30 (sinex x))
In an equation for ‘sinf’: sinf x = foldl (+) (take 30 (sinex x))
Upvotes: 3
Views: 644
Reputation: 120711
Here's a more reasonable way to write it all:
sinf :: Double -> Double
sinf x = sum $ take 30
[ sign * power / fact
| (sign, power, fact) <- zip3
(iterate negate 1)
(oddEntries $ iterate (*x) 1)
(oddEntries factorials) ]
where factorials :: [Double]
factorials = scanl (*) 1 [1..]
oddEntries :: [a] -> [a]
oddEntries (_:x:xs) = x : oddEntries xs
oddEntries _ = []
Note that I didn't need any fromIntegral
, because I declared the list of factorials as Double
. That's of course not precise, but it doesn't matter in this application (the error is anyway dominated by the truncation of the series) and it's much more efficient for big numbers than converting from Integer
.
Also I avoided the ^
operator, which is quite wasteful in this case: the intermediate results of the multiplication are anyways needed in the Maclaurin series, so better also do this with iterated multiplication!
Incidentally, you can then also omit the local binding and just write it all inline:
sinf :: Double -> Double
sinf x = sum $ take 30
[ sign * power / fact
| (sign, power, fact) <- zip3
(iterate negate 1)
(oddEntries $ iterate (*x) 1)
(oddEntries $ scanl (*) 1 [1..]) ]
Here the typechecker automatically does the right thing and chooses the type Double
for the list of factorials (as well as the list of signs), because the context requires Double
. (But it can be a good idea to nevertheless give it a local name and signature, for the sake of readability.)
$ ghci wtmpf-file3770.hs
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/sagemuej/.ghc/ghci.conf
Loaded GHCi configuration from /home/sagemuej/.ghci
[1 of 1] Compiling Main ( wtmpf-file3770.hs, interpreted )
Ok, 1 module loaded.
*Main> :m +Graphics.Dynamic.Plot.R2
*Main Graphics.Dynamic.Plot.R2> plotWindow [legendName "sin" $ continFnPlot sin, legendName "sinf" $ continFnPlot sinf]
In practice, you'd only use the Maclaurin series in the small range around zero, and exploit the periodicity for defining the function on the remainder of the real axis:
sinf :: Double -> Double
sinf x = sum $ take 30
[ sign * power / fact
| (sign, power, fact) <- zip3
(iterate negate 1)
(oddEntries $ iterate (*x') 1)
(oddEntries factorials) ]
where factorials :: [Double]
factorials = scanl (*) 1 [1..]
x' = x - 2*pi*fromIntegral (round $ x/(2*pi))
Upvotes: 1
Reputation: 54981
After your edits, looking at the first error:
No instance for (Fractional Integer) arising from a use of ‘/’
The type of (/)
is:
(/) :: Fractional a => a -> a -> a
So it takes two values of type a
, which must be Fractional
, and returns a result of the same type a
. Integer
is not Fractional
—there are integer division functions div
and quot
(and their remainder counterparts, mod
and rem
), but that’s not what you want here, because you want a fractional result. The solution is to convert the integer argument to a fractional type such as Double
using fromIntegral
:
fromIntegral :: (Num b, Integral a) => a -> b
fromIntegral (x :: Integer) :: Double -- or ‘… :: Float’
So for example you could write sum xs / fromIntegral (length xs)
to get the mean of a list of Double
.
The second error:
Could not deduce (Num [Integer]) arising from a use of ‘+’
Arises from this expression:
foldl (+) (take 30 (sinex x))
foldl
takes three arguments: the reducing function, the starting value, and the input container; you’re trying to pass the container as the starting value, so foldl
tries to call +
on a list of integers, which is not defined. You might want foldl (+) 0
or sum
there.
Upvotes: 2
Reputation: 60463
Numerics in Haskell have a lot to do with types and type inference, so they can be tricky for beginners. You're not allowed to divide integers at all with the /
operator (there is `div`
for truncating integer division). What makes a constant an integer can also be rather tricky, as it depends on how it is used (later!)1. But the main thing you need to know about is the fromIntegral
function, which casts from an integer into any other numeric type.
ghci> let x = 10 :: Integer
ghci> x / 5
<interactive>:6:1: error:
• No instance for (Fractional Integer) arising from a use of ‘/’
• In the expression: x / 5
In an equation for ‘it’: it = x / 5
ghci> fromIntegral x / 5
2.0
And @leftroundabout's comment is absolutely right! Type inference is lovely but if you let it run wild it can be very confusing; rein it in with type signatures.
1Here's one thing that went wrong, if you are interested. Maybe it will help with understanding how types and numbers interact.
(^)
takes an integer (technically any "Integral
" type, which defaults to Integer
if there is nothing else to make the decision) as its right argument, so we know from
powerl x = map (x^) oddl
that oddl
must be a list of integers. (*)
returns the same type as its arguments (which must be the same type), so we know from
factorial x = factorial (x - 1) * x
that factorial
returns the same type as its argument, and thus
factl = map factorial oddl
must also be a list of integers. Then we have
sinex x = zipWith (/) sined factl
which is now giving integers as an argument to (/)
, which is not legal.
Upvotes: 3