Send Beginners mailing list submissions to
[email protected]
To subscribe or unsubscribe via the World Wide Web, visit
http://www.haskell.org/mailman/listinfo/beginners
or, via email, send a message with subject or body 'help' to
[email protected]
You can reach the person managing the list at
[email protected]
When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."
Today's Topics:
1. Capture stdout from a command on Windows (David Frey)
2. Re: Padding List with Zeros (Hein Hundal)
3. Re: The cost of generality, or how expensive is realToFrac?
(Greg)
4. Re: Padding List with Zeros (Daniel Fischer)
5. Re: Capture stdout from a command on Windows (Maciej Podgurski)
6. Re: The cost of generality, or how expensive is realToFrac?
(Daniel Fischer)
----------------------------------------------------------------------
Message: 1
Date: Wed, 15 Sep 2010 11:08:58 -0700
From: David Frey <[email protected]>
Subject: [Haskell-beginners] Capture stdout from a command on Windows
To: <[email protected]>
Message-ID: <f9539dc016c3a57a9ee57ec32f67c...@localhost>
Content-Type: text/plain; charset=UTF-8
Hi,
I'm trying to capture the output (stdout) of a command on Windows.
This is what I have so far:
import System.Process (runProcess, waitForProcess)
import System.IO (hGetContents)
executeCapturingStdout :: String -> String -> IO [String]
executeCapturingStdout cmd workingDir = do
let stdoutHandle = -- TODO: What do I put here?
processHandle <- runProcess cmd [] (Just workingDir) Nothing Nothing
stdoutHandle Nothing
exitCode <- waitForProcess processHandle
content <- hGetContents stdoutHandle
return lines content
I have tried searching on google and I haven't been able to find an answer
yet.
Thanks,
Dave
------------------------------
Message: 2
Date: Wed, 15 Sep 2010 11:24:09 -0700 (PDT)
From: Hein Hundal <[email protected]>
Subject: Re: [Haskell-beginners] Padding List with Zeros
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=us-ascii
On Wed, 15 Sep 2010 16:23:49, Daniel Fischer wrote:
> On Wednesday 15 September 2010 15:15:49, Henry Olders wrote:
> > On 2010-09-14, at 19:35 , Lorenzo Isella wrote:
> > > Dear All,
> > > I still have to find my way with immutable lists and list
> > > comprehension. Consider the following lists
> > >
> > > A=[0,10,20,30,40,50]
> > > B=[0,10,50] (i.e. B is a subset of list A; list A is
> > > already ordered in increasing order and so is B).
> > > C=[2,1,-5] i.e. there is a corresponding element in C for every
> > > element in B.
> > >
> > > Now, I would like to define a new list D having length equal
> > > to the length of A. The elements of D in the position of the
> > > elements of A in common with B are equal to the corresponding
> > > entries in C, whereas the other ones are zero i.e.
> > > D=[2,1,0,0,0,-5]. How can I achieve that? The first thought
> > > that comes to my mind is to define a list of zeros which I
> > > would modify according to my needs, but that is not allowed...
Yes, that is not allowed. First thing I thought of also.
> > > Many thanks
> > >
> > > Lorenzo
> >
> > Being a real Haskell newby, I can figure out a one-line solution in
> > Python, but I don't know how to do something similar in Haskell, or even
> > if it's possible. Please correct me if I'm wrong, but there does not
> > seem to be a dictionary type in Haskell, and I am not aware of how to
> > specify an inline if...else inside a list comprehension. I would really
> > appreciate it if someone could show me how to do something similar to
> > this Python statement in Haskell.
> >
>
>import Data.Maybe
>
> > >>> A=[0,10,20,30,40,50]
> > >>> B=[0,10,50]
> > >>> C=[2,1,-5]
>
> These have to be lowercase in Haskell, of course :)
>
> > >>> [dict(zip(B,C))[a] if a in B else 0 for a in A]
>
> map (fromMaybe 0 . (`lookup` zip b c)) a
>
> or, as a list comprehension,
>
> [fromMaybe 0 (lookup v dic) | let dic = zip b c, v <- a]
>
> Slightly more verbose than the Python.
>
> But this doesn't deal with multiple entries (istr that was
> mentioned previously in this thread), for
>
> a = [0, 10, 10, 20, 30 , 40, 50]
> b = [0, 10, 10, 50]
> c = [2, 1, 3, -5]
>
> neither would produce
>
> [2, 1, 3, 0, 0, 0, -5]
>
> which I believe would be the desired behavior.
>
> >
> > [2, 1, 0, 0, 0, -5]
> >
> > Henry
I love the map solution and the lookup solutions--very concise. Someday
perhaps those will occur to me when I look at these problems.
Here is my (extremely) verbose beginner solution. I think this solution is
linear time and it returns the "desired behavior" in Daniel's post.
-- indices v1 v2
-- find the elemIndex of v2's elements in v1
-- almost equivalent to (map (flip elemIndex v1) v2)
--
indices v1 v2 = indices' 0 v1 v2
indices' iOff (x:xs) (y:ys)
| x < y = indices' (iOff+1) xs (y:ys)
| x ==y = iOff:(indices' (iOff+1) xs ys)
| x > y = error "indicies:: elem not found"
indices' _ _ [] = []
indices' _ [] (y:ys) = error "indices:: elem not found"
-- makevec 0 indices values
-- returns a vector with values filled in at the indices given
--
makevec _ [] _ = []
makevec _ _ [] = error "makevec:: "
makevec iOffSet (i:is) (x:xs)
| iOffSet < i = replicate (i-iOffSet) 0 ++ makevec i (i:is) (x:xs)
| iOffSet ==i = x:(makevec (iOffSet+1) is xs)
| iOffSet > i = error "makevec error"
hisfunc :: [Integer] -> [Integer] -> [Integer] -> [Integer]
hisfunc a b c = let front = makevec 0 (indices a b) c
in front ++ replicate (length a - length front) 0
test1 = hisfunc a b c
test2 = hisfunc (a++[70, 90]) b c
test3 = hisfunc (a++[70, 90]) (b++[70]) (c++[-14])
test4 = hisfunc [0,10,10,20,30,40,50] [0,10,10,50] [2,1,3,-5]
------------------------------
Message: 3
Date: Wed, 15 Sep 2010 11:50:13 -0700
From: Greg <[email protected]>
Subject: Re: [Haskell-beginners] The cost of generality, or how
expensive is realToFrac?
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=us-ascii
Hey, thanks, Daniel.
I hadn't come across rewrite rules yet. They definitely look like something
worth learning, though I'm not sure I'm prepared to start making custom
versions of OpenGL.Raw...
It looks like I managed to put that battle off for another day, however. I did
look at how realToFrac is implemented and (as you mention) it does the
fromRational . toRational transform pair suggested in a number of sources,
including Real World Haskell. Looking at what toRational is doing, creating a
ratio of integers out of a float it seems like a crazy amount of effort to go
through just to convert floating point numbers.
Looking at the RealFloat class rather that Real and Fractional, it seems like
this is a much more efficient way to go:
floatToFloat :: (RealFloat a, RealFloat b) => a -> b
floatToFloat = (uncurry encodeFloat) . decodeFloat
I substituted this in for realToFrac and I'm back to close to my original
performance. Playing with a few test cases in ghci, it looks numerically
equivalent to realToFrac.
This begs the question though-- am I doing something dangerous here? Why isn't
this the standard approach?
If I understand what's happening, decodeFloat and encodeFloat are breaking the
floating point numbers up into their constituent parts-- presumably by bit
masking the raw binary. That would explain the performance improvement. I
suppose there is some implementation dependence here, but as long as the encode
and decode are implemented as a matched set then I think I'm good.
Cheers--
Greg
On Sep 15, 2010, at 1:56 AM, Daniel Fischer wrote:
> On Wednesday 15 September 2010 02:51:01, Greg wrote:
>> First, to anyone who recognizes me by name, thanks to the help I've been
>> getting here I've managed to put together a fairly complex set of code
>> files that all compile together nicely, run, and do exactly what I
>> wanted them to do. Success!
>>
>> The trouble is that my implementation is dog slow
>>
>> Fortunately, this isn't the first time I've been in over my head and I
>> started by putting up some simpler scaffolding- which runs much more
>> quickly. Working backwards, it looks like the real bottle neck is in
>> the data types I've created, the type variables I've introduced, and the
>> conversion code I needed to insert to make it all happy.
>>
>> I'm not sure it helps, but I've attached a trimmed down version of the
>> relevant code. What should be happening is my pair is being converted
>> to the canonical form for Coord2D which is Cartesian2D and then
>> converted again to Vertex2. There shouldn't be any change made to the
>> values, they're only being handed from one container to another in this
>> case (Polar coordinates would require computation, but I've stripped
>> that out for the time being). However, those handoffs require calls to
>> realToFrac to make the type system happy, and that has to be what is
>> eating up all my CPU.
>
> Not all, but probably a big chunk of it.
> The problem is that the default implementation of realToFrac is
>
> realToFrac = fromRational . toRational
>
> a) with that implementation, realToFrac :: Double -> Double is not the
> identity (doesn't respect NaNs)
> b) it's slow, there are no special operations to convert Double, Float etc.
> from/to Rational.
>
> For a lot of types, GHC provides rewrite rules (you need to compile with
> optimisations to have them fire) which give faster versions (with somewhat
> different behaviour, e.g. realToFrac :: Double -> Double is rewritten to
> id, realToFrac between Float and Double uses primitive widening/narrowing
> ops, for several newtype wrappers around Float/Double there are rules too).
>
>>
>> I think there are probably 4 calls to realToFrac. If I walk through the
>> code, the result, given the pair p, should be: Vertex2 (realToFrac
>> (realToFrac (fst p))) (realToFrac (realToFrac (snd p)))
>>
>> I'd like to maintain type independence if possible, but I expect most
>> uses of this code to feed Doubles in for processing and probably feed
>> GLclampf (Floats, I believe)
>
> newtype wrapper around CFloat, which is a newtype wrapper around Float
>
> Unfortunately, there are no rewrite rules in the module where it is
> defined, apparently neither any other module that has access to the
> constructor. And the constructor is not accessible from any of the exposed
> modules, so as far as I know, you can't provide your own rewrite rules.
>
>> to the OpenGL layer. If there's a way to
>> do so, I wouldn't mind optimizing for that particular set of types.
>> I've tried GLdouble, and it doesn't really improve things with the
>> current code.
>>
>> Is there a way to short circuit those realToFrac calls if we know the
>> input and output are the same type? Is there a way merge the nested
>> calls?
>
> You can try rewrite rules
>
> {-# RULES
> "realToFrac2/realToFrac" realToFrac . realToFrac = realToFrac
> "realToFrac/id" realToFrac = id
> #-}
>
> but I'm afraid the second won't work at all, then you'd have to specify all
> interesting cases yourself (there are rules for the cases Double -> Double
> and Float -> Float in GHC.Float, rules for converting from/to CFloat and
> CDouble in Foreign.C.Types, so those should be fine too)
> "realToFrac/GLclampf->GLclampf" realToFrac = id :: GLclampf -> GLclampf
> and what ese you need.
> Whether the first one will help (or even work), I don't know either, you
> have to try.
>
>>
>> Any other thoughts on what I can do here? The slow down between the two
>> implementations is at least 20x, which seems like a steep penalty to
>> pay.
>
> In case of emergency, put the needed rewrite rules into the source of
> OpenGLRaw yourself.
>
>>
>> And while I'm at it, is turning on FlexibleInstances the only way to
>> create an instance for (a,a)?
>
> Yes. Haskell98 doesn't allow such instance declarations, so you need the
> extension.
>
------------------------------
Message: 4
Date: Wed, 15 Sep 2010 21:12:09 +0200
From: Daniel Fischer <[email protected]>
Subject: Re: [Haskell-beginners] Padding List with Zeros
To: [email protected]
Cc: Hein Hundal <[email protected]>
Message-ID: <[email protected]>
Content-Type: text/plain; charset="iso-8859-1"
On Wednesday 15 September 2010 20:24:09, Hein Hundal wrote:
> On Wed, 15 Sep 2010 16:23:49, Daniel Fischer wrote:
> > On Wednesday 15 September 2010 15:15:49, Henry Olders wrote:
> > > On 2010-09-14, at 19:35 , Lorenzo Isella wrote:
> > > > Dear All,
> > > > I still have to find my way with immutable lists and list
> > > > comprehension. Consider the following lists
> > > >
> > > > A=[0,10,20,30,40,50]
> > > > B=[0,10,50] (i.e. B is a subset of list A; list A is
> > > > already ordered in increasing order and so is B).
> > > > C=[2,1,-5] i.e. there is a corresponding element in C for every
> > > > element in B.
> > > >
> > > > Now, I would like to define a new list D having length equal
> > > > to the length of A. The elements of D in the position of the
> > > > elements of A in common with B are equal to the corresponding
> > > > entries in C, whereas the other ones are zero i.e.
> > > > D=[2,1,0,0,0,-5]. How can I achieve that? The first thought
> > > > that comes to my mind is to define a list of zeros which I
> > > > would modify according to my needs, but that is not allowed...
>
> Yes, that is not allowed. First thing I thought of also.
>
> > > > Many thanks
> > > >
> > > > Lorenzo
> > >
> > > Being a real Haskell newby, I can figure out a one-line solution in
> > > Python, but I don't know how to do something similar in Haskell, or
> > > even if it's possible. Please correct me if I'm wrong, but there
> > > does not seem to be a dictionary type in Haskell, and I am not aware
> > > of how to specify an inline if...else inside a list comprehension. I
> > > would really appreciate it if someone could show me how to do
> > > something similar to this Python statement in Haskell.
> >
> >import Data.Maybe
> >
> > > >>> A=[0,10,20,30,40,50]
> > > >>> B=[0,10,50]
> > > >>> C=[2,1,-5]
> >
> > These have to be lowercase in Haskell, of course :)
> >
> > > >>> [dict(zip(B,C))[a] if a in B else 0 for a in A]
> >
> > map (fromMaybe 0 . (`lookup` zip b c)) a
> >
> > or, as a list comprehension,
> >
> > [fromMaybe 0 (lookup v dic) | let dic = zip b c, v <- a]
> >
> > Slightly more verbose than the Python.
> >
> > But this doesn't deal with multiple entries (istr that was
> > mentioned previously in this thread), for
> >
> > a = [0, 10, 10, 20, 30 , 40, 50]
> > b = [0, 10, 10, 50]
> > c = [2, 1, 3, -5]
> >
> > neither would produce
> >
> > [2, 1, 3, 0, 0, 0, -5]
> >
> > which I believe would be the desired behavior.
> >
> > > [2, 1, 0, 0, 0, -5]
> > >
> > > Henry
>
> I love the map solution and the lookup solutions--very concise. Someday
> perhaps those will occur to me when I look at these problems.
>
> Here is my (extremely) verbose beginner solution. I think this solution
> is linear time and it returns the "desired behavior" in Daniel's post.
>
> -- indices v1 v2
> -- find the elemIndex of v2's elements in v1
> -- almost equivalent to (map (flip elemIndex v1) v2)
> --
> indices v1 v2 = indices' 0 v1 v2
> indices' iOff (x:xs) (y:ys)
>
> | x < y = indices' (iOff+1) xs (y:ys)
> | x ==y = iOff:(indices' (iOff+1) xs ys)
> | x > y = error "indicies:: elem not found"
>
> indices' _ _ [] = []
> indices' _ [] (y:ys) = error "indices:: elem not found"
>
>
> -- makevec 0 indices values
> -- returns a vector with values filled in at the indices given
> --
> makevec _ [] _ = []
> makevec _ _ [] = error "makevec:: "
> makevec iOffSet (i:is) (x:xs)
>
> | iOffSet < i = replicate (i-iOffSet) 0 ++ makevec i (i:is) (x:xs)
> | iOffSet ==i = x:(makevec (iOffSet+1) is xs)
> | iOffSet > i = error "makevec error"
>
> hisfunc :: [Integer] -> [Integer] -> [Integer] -> [Integer]
> hisfunc a b c = let front = makevec 0 (indices a b) c
> in front ++ replicate (length a - length front) 0
>
>
> test1 = hisfunc a b c
> test2 = hisfunc (a++[70, 90]) b c
> test3 = hisfunc (a++[70, 90]) (b++[70]) (c++[-14])
> test4 = hisfunc [0,10,10,20,30,40,50] [0,10,10,50] [2,1,3,-5]
Somewhat simpler in one go:
-- Preconditions:
-- length bs == length cs
-- as and bs are sorted
-- bs is a sublist of as
expand :: (Ord a, Num a) => [a] -> [a] -> [a] -> [a]
expand as [] _ = map (const 0) as
expand (a:as) bbs@(b:bs) ccs@(c:cs)
| a < b = 0 : expand as bbs ccs
| otherwise = c : expand as bs cs
expand _ _ _ = []
------------------------------
Message: 5
Date: Wed, 15 Sep 2010 21:49:19 +0200
From: Maciej Podgurski <[email protected]>
Subject: Re: [Haskell-beginners] Capture stdout from a command on
Windows
To: David Frey <[email protected]>
Cc: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=UTF-8; format=flowed
Hi,
you might use runInteractiveProcess which returns the process handle and
three handles hIn, hOut and hErr for communication with the process.
Besh wishes,
Maciej
David Frey wrote:
> Hi,
>
> I'm trying to capture the output (stdout) of a command on Windows.
>
> This is what I have so far:
>
> import System.Process (runProcess, waitForProcess)
> import System.IO (hGetContents)
>
> executeCapturingStdout :: String -> String -> IO [String]
> executeCapturingStdout cmd workingDir = do
> let stdoutHandle = -- TODO: What do I put here?
> processHandle <- runProcess cmd [] (Just workingDir) Nothing Nothing
> stdoutHandle Nothing
> exitCode <- waitForProcess processHandle
> content <- hGetContents stdoutHandle
> return lines content
>
> I have tried searching on google and I haven't been able to find an answer
> yet.
>
> Thanks,
> Dave
> _______________________________________________
> Beginners mailing list
> [email protected]
> http://www.haskell.org/mailman/listinfo/beginners
>
------------------------------
Message: 6
Date: Thu, 16 Sep 2010 00:36:11 +0200
From: Daniel Fischer <[email protected]>
Subject: Re: [Haskell-beginners] The cost of generality, or how
expensive is realToFrac?
To: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset="iso-8859-1"
On Wednesday 15 September 2010 20:50:13, Greg wrote:
> I hadn't come across rewrite rules yet. They definitely look like
> something worth learning,
Absolutely. GHC's optimiser is good, but there are a lot of cases where you
need to push it via rewrite rules if you write polymorphic code or if you
want to eliminate intermediate data structures (e.g. list fusion).
> though I'm not sure I'm prepared to start making custom versions
> of OpenGL.Raw...
Yes, if you can work around the issues without that, it's better to leave
it in peace :)
Though you might ask the maintainer for rewrite rules.
>
> It looks like I managed to put that battle off for another day, however.
> I did look at how realToFrac is implemented and (as you mention) it
> does the fromRational . toRational transform pair suggested in a number
> of sources, including Real World Haskell. Looking at what toRational is
> doing, creating a ratio of integers out of a float it seems like a crazy
> amount of effort to go through just to convert floating point numbers.
I just did some benchmarking.
I benchmarked
foldl' (+) 0 [convert (1 / intToDoub k) | k <- [1 .. 100000]]
where
intToDoub :: Int -> Double
intToDoub = fromIntegral
for several functions
convert :: Double -> Float (actually, the type has been
(RealFloat a, RealFloat b) => a -> b, but it was used at a = Double,
b = Float).
Everything was compiled with -O2, so the rewrite rules fired, in particular
intToDoub was replaced by a primop (int2Double#), so that one's ultra
cheap.
For convert = realToFrac (hence by the rewrite rules the primop
double2Float# was used), I got pretty good times, mean was 6.76 ms.
For convert = floatToFloat from below, the times were not too bad, with a
mean of 26.3 ms.
A factor of roughly four for this benchmark (the factor for the conversion
itself will be higher, but not exorbitantly) means it's usable in many
situations, but not in performance critical situations where the conversion
takes a significant amount of the running time. If you're converting to
draw stuff with OpenGL (or some other graphics library), the conversion
will take only a relatively small part of the time, so it's fine.
For convert = fromRational . toRational (so no rewrite rules), the times
were rather appalling: mean was 3.34 seconds.
A factor of nearly 500 versus double2Float#.
toRational is bad. Looking at
instance Real Double where
toRational x = (m%1)*(b%1)^^n
where (m,n) = decodeFloat x
b = floatRadix x
(same for Float), and the implementations of (^^) and (^),
{-# SPECIALISE (^) ::
Integer -> Integer -> Integer,
Integer -> Int -> Integer,
Int -> Int -> Int #-}
(^) :: (Num a, Integral b) => a -> b -> a
x0 ^ y0 | y0 < 0 = error "Negative exponent"
| y0 == 0 = 1
| otherwise = f x0 y0
where -- f : x0 ^ y0 = x ^ y
f x y | even y = f (x * x) (y `quot` 2)
| y == 1 = x
| otherwise = g (x * x) ((y - 1) `quot` 2) x
-- g : x0 ^ y0 = (x ^ y) * z
g x y z | even y = g (x * x) (y `quot` 2) z
| y == 1 = x * z
| otherwise = g (x * x) ((y - 1) `quot` 2) (x * z)
-- | raise a number to an integral power
{-# SPECIALISE (^^) ::
Rational -> Int -> Rational #-}
(^^) :: (Fractional a, Integral b) => a -> b -> a
x ^^ n = if n >= 0 then x^n else recip (x^(negate n))
together with the multiplication and recip for Rationals, I have to say
ouch!
There's no special implementation and rewrite rule for powers of Rationals,
so on each multiplication in (^), the gcd of numerator and denominator is
calculated, *although as powers of the original numerator and denominator
they are guaranteed to be coprime*. Considering how slow a division of
Integers is, awwwwwww noooooo.
So let's look at a better implementation of toRational:
toRat :: RealFloat a => a -> Rational
toRat x = case decodeFloat x of
(m,e) -> case floatRadix x of
b -> if e < 0
then (m % (b^(negate e)))
else (m * b^e) :% 1
(inlined a better implementation of powers for Rationals).
Benchmarking convert = fromRational . toRat show a significant improvement,
the mean dropped to 2.75 seconds.
Still appalling, but it's a nice improvement and I don't see any quick
opportunities to improve that conversion.
So let's come to the last, fromRational. That's a compicated function, and
unfortunately it has to be and I've no good idea to improve it.
fromRational is really evil (in terms of clock cycles).
Replacing fromRational with a dummy that just forces the evaluation of its
argument and returns NaN, ±Infinity, or 0 for all real Rational values,
dummy . toRational had a mean of 623.5 ms and
dummy . toRat had a mean of 200.7 ms.
So toRat is a jolly good improvement over toRational, but it's still
awfully slow. And since fromRational takes much much longer anyway, it's a
not too impressive gain for realToFrac.
>
> Looking at the RealFloat class rather that Real and Fractional, it seems
> like this is a much more efficient way to go:
>
> floatToFloat :: (RealFloat a, RealFloat b) => a -> b
> floatToFloat = (uncurry encodeFloat) . decodeFloat
Yes, that's much more efficient, as witnessed by the benchmark results.
But.
>
> I substituted this in for realToFrac and I'm back to close to my
> original performance. Playing with a few test cases in ghci, it looks
> numerically equivalent to realToFrac.
>
> This begs the question though--
No. Sorry, but I can't bear that misuse:
http://en.wikipedia.org/wiki/Begging_the_question
It raises/demands/invites the question, but it doesn't beg it.
> am I doing something dangerous here?
Yes and no.
> Why isn't this the standard approach?
Because it will wreak unspeakable havoc when someone creates a RealFloat
instance with a floatRadix > 2. A floatRadix of 10 or some power of 2 (16,
256?) could be even reasonable.
But for conversions between RealFloat types with the same floatRadix, it's
sort of okay, only it clobbers NaNs and (for some conversions) Infinities.
However, realToFrac does that too.
>
> If I understand what's happening, decodeFloat and encodeFloat are
> breaking the floating point numbers up into their constituent parts--
> presumably by bit masking the raw binary.
Probably.
> That would explain the
> performance improvement. I suppose there is some implementation
> dependence here, but as long as the encode and decode are implemented as
> a matched set then I think I'm good.
Not entirely matched, for Float -> Float and Double -> Double,
NaN -> -Infinity, maybe denormalized values break too.
±Infinity is mapped to a finite value at Float -> Double
But since toRational uses decodeFloat and fromRational uses encodeFloat,
floatToFloat is in that respect no worse than realToFrac without rewrite
rules.
>
> Cheers--
> Greg
------------------------------
_______________________________________________
Beginners mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/beginners
End of Beginners Digest, Vol 27, Issue 37
*****************************************