Haskell type Converting errors - haskell

a newbie and have made this code in winHugs Haskell to check the maximum exponent k can be induced, that 2 power k is the divisor of n:
maxexp2 ::Int -> Int
maxexp2 n
| n==0 || 2^k`mod`n /= 0 = 0
| otherwise == k
where k = e `div` f
e = round (fromIntegral(log n))
f = round (fromIntegral (log 2))
Somehow I know there is something fishy with the e and f part ...the error says : fractional Int instance is needed for the execution of this function.
I don't really understand that...can someone please explain it to me ?

Obviously, the logarithm of almost all numbers is something irrational, so using fromIntegral on it should indeed seem fishy to you. Why do you even think you need it on that point? Where you do need it is before the logarithm, because that actually also accepts only values of the Floating class, as we can easily find out1:
Prelude> :t log
log :: Floating a => a -> a
So it hase to be e = round (log $ fromIntegral n). You don't need it for f at all, because 2 is just a generic Num literal, not an Int. Obviously though, it can't be right to just round the log 2, because that is simply 1. What you probably want is round $ e / f with floating-point e and f.
1I don't know if this works in Hugs; probably. If not, use GHCi (why does everybody new seem to use Hugs at the moment anyway? I don't know why you would use anything else but GHC).

Related

Using recursion schemes in Haskell for solving change making problem

I'm trying to understand histomorphisms from this blog on recursion schemes. I'm facing a problem when I'm running the example to solve the change making problem as mentioned in the blog.
Change making problem takes the denominations for a currency and tries to find the minimum number of coins required to create a given sum of money. The code below is taken from the blog and should compute the answer.
{-# LANGUAGE DeriveFunctor #-}
module Main where
import Control.Arrow ( (>>>) )
import Data.List ( partition )
import Prelude hiding (lookup)
newtype Term f = In {out :: f (Term f)}
data Attr f a = Attr
{ attribute :: a
, hole :: f (Attr f a)
}
type CVAlgebra f a = f (Attr f a) -> a
histo :: Functor f => CVAlgebra f a -> Term f -> a
histo h = out >>> fmap worker >>> h
where
worker t = Attr (histo h t) (fmap worker (out t))
type Cent = Int
coins :: [Cent]
coins = [50, 25, 10, 5, 1]
data Nat a
= Zero
| Next a
deriving (Functor)
-- Convert from a natural number to its foldable equivalent, and vice versa.
expand :: Int -> Term Nat
expand 0 = In Zero
expand n = In (Next (expand (n - 1)))
compress :: Nat (Attr Nat a) -> Int
compress Zero = 0
compress (Next (Attr _ x)) = 1 + compress x
change :: Cent -> Int
change amt = histo go (expand amt)
where
go :: Nat (Attr Nat Int) -> Int
go Zero = 1
go curr#(Next attr) =
let given = compress curr
validCoins = filter (<= given) coins
remaining = map (given -) validCoins
(zeroes, toProcess) = partition (== 0) remaining
results = sum (map (lookup attr) toProcess)
in length zeroes + results
lookup :: Attr Nat a -> Int -> a
lookup cache 0 = attribute cache
lookup cache n = lookup inner (n - 1) where (Next inner) = hole cache
Now if you evaluate change 10 it will give you 3.
Which is... incorrect because you can make 10 using 1 coin of value 10.
So I considered maybe it's solving the coin change problem, which finds the maximum number of ways in which you can make the given sum of money. For e.g. you can make 10 in 4 ways with { 1, 1, ... 10 times }, { 1, 1, 1, 1, 5}, { 5, 5 }, { 10 }.
So what is wrong with this piece of code? Where is it going wrong in solving the problem?
TLDR
The above piece of code from this blog on recursion schemes is not finding minimum or maximum ways to change a sum of money. Why is it not working?
I put some more thought into encoding this problem with recursion schemes. Maybe there's a good way to solve the unordered problem (i.e., considering 5c + 1c to be different from 1c + 5c) using a histomorphism to cache the undirected recursive calls, but I don't know what it is. Instead, I looked for a way to use recursion schemes to implement the dynamic-programming algorithm, where the search tree is probed in a specific order so that you're sure you never visit any node more than once.
The tool that I used is the hylomorphism, which comes up a bit later in the article series you're reading. It composes an unfold (anamorphism) with a fold (catamorphism). A hylomorphism uses ana to build up an intermediate structure, and then cata to tear it down into a final result. In this case, the intermediate structure I used describes a subproblem. It has two constructors: either the subproblem is solved already, or there is some amount of money left to make change for, and a pool of coin denominations to use:
data ChangePuzzle a = Solved Int
| Pending {spend, forget :: a}
deriving Functor
type Cent = Int
type ChangePuzzleArgs = ([Cent], Cent)
We need a coalgebra that turns a single problem into subproblems:
divide :: Coalgebra ChangePuzzle ChangePuzzleArgs
divide (_, 0) = Solved 1
divide ([], _) = Solved 0
divide (coins#(x:xs), n) | n < 0 = Solved 0
| otherwise = Pending (coins, n - x) (xs, n)
I hope the first three cases are obvious. The last case is the only one with multiple subproblems. We can either use one coin of the first listed denomination, and continue to make change for that smaller amount, or we can leave the amount the same but reduce the list of coin denominations we're willing to use.
The algebra for combining subproblem results is much simpler: we simply add them up.
conquer :: Algebra ChangePuzzle Int
conquer (Solved n) = n
conquer (Pending a b) = a + b
I originally tried to write conquer = sum (with the appropriate Foldable instance), but this is incorrect. We're not summing up the a types in the subproblem; rather, all the interesting values are in the Int field of the Solved constructor, and sum doesn't look at those because they're not of type a.
Finally, we let recursion schemes do the actual recursion for us with a simple hylo call:
waysToMakeChange :: ChangePuzzleArgs -> Int
waysToMakeChange = hylo conquer divide
And we can confirm it works in GHCI:
*Main> waysToMakeChange (coins, 10)
4
*Main> waysToMakeChange (coins, 100)
292
Whether you think this is worth the effort is up to you. Recursion schemes have saved us very little work here, as this problem is easy to solve by hand. But you may find reifying the intermediate states makes the recursive structure explicit, instead of implicit in the call graph. Anyway it's an interesting exercise if you want to practice recursion schemes in preparation for more complicated tasks.
The full, working file is included below for convenience.
{-# LANGUAGE DeriveFunctor #-}
import Control.Arrow ( (>>>), (<<<) )
newtype Term f = In {out :: f (Term f)}
type Algebra f a = f a -> a
type Coalgebra f a = a -> f a
cata :: (Functor f) => Algebra f a -> Term f -> a
cata fn = out >>> fmap (cata fn) >>> fn
ana :: (Functor f) => Coalgebra f a -> a -> Term f
ana f = In <<< fmap (ana f) <<< f
hylo :: Functor f => Algebra f b -> Coalgebra f a -> a -> b
hylo alg coalg = ana coalg >>> cata alg
data ChangePuzzle a = Solved Int
| Pending {spend, forget :: a}
deriving Functor
type Cent = Int
type ChangePuzzleArgs = ([Cent], Cent)
coins :: [Cent]
coins = [50, 25, 10, 5, 1]
divide :: Coalgebra ChangePuzzle ChangePuzzleArgs
divide (_, 0) = Solved 1
divide ([], _) = Solved 0
divide (coins#(x:xs), n) | n < 0 = Solved 0
| otherwise = Pending (coins, n - x) (xs, n)
conquer :: Algebra ChangePuzzle Int
conquer (Solved n) = n
conquer (Pending a b) = a + b
waysToMakeChange :: ChangePuzzleArgs -> Int
waysToMakeChange = hylo conquer divide
The initial confusion with the blog post was because it was pointing to a different problem in the wikipedia link.
Retaking a look at change, it's trying to find the number of "ordered" ways of making change for a given value. This means that the ordering of coins matters. The correct value of change 10 should be 9.
Coming back to the problem, the main issue is with the implementation of the lookup method. The key point to note is that lookup is backwards i.e to calculate the contribution of a denomination to the sum it should be passed as argument to the lookup and not it's difference with the given value.
-- to find contribution of 5 to the number of ways we can
-- change 15. We should pass the cache of 15 and 5 as the
-- parameters. So the cache will be unrolled 5 times to
-- to get the value from cache of 10
lookup :: Attr Nat a -- ^ cache
-> Int -- ^ how much to roll back
-> a
lookup cache 1 = attribute cache
lookup cache n = lookup inner (n - 1) where (Next inner) = hole cache
The complete solution is described in this issue by #howsiwei.
Edit: Base on discussion in the comments this can be solved using histomorphisms but with a few challenges
It can be solved using histomorphisms but the cache and functor types will need to be more complex to hold more state. Namely -
The cache will need to keep a list of permitted denominations for a particular amount this will allow us eliminate overlap
The harder challenge is to come up with a functor that can order all the information. Nat will not be sufficient because it cannot distinguish between different values of a complex cache type.
I see two problems with this program. One of them I know how to fix, but the other apparently requires more knowledge of recursion schemes than I have.
The one I can fix is that it's looking up the wrong values in its cache. When given = 10, of course validCoins = [10,5,1], and so we find (zeroes, toProcess) = ([0], [5,9]). So far so good: we can give a dime directly, or give a nickel and then make change for the remaining five cents, or we can give a penny and change the remaining nine cents. But then when we write lookup 9 attr, we're saying "look 9 steps in history to when curr = 1", where what we meant was "look 1 step into history to when curr = 9". As a result we drastically undercount in pretty much all cases: even change 100 is only 16, while a Google search claims the right result is 292 (I haven't verified this today by implementing it myself).
There are a few equivalent ways to fix this; the smallest diff would be to replace
results = sum (map (lookup attr)) toProcess)
with
results = sum (map (lookup attr . (given -)) toProcess)
The second problem is: the values in the cache are wrong. As I mentioned in a comment on the question, this counts different orderings of the same denominations as separate answers to the question. After I fix the first problem, the lowest input where this second problem manifests is 7, with the incorrect result change 7 = 3. If you try change 100 I don't know how long it takes to compute: much longer than it should, probably a very long time. But even a modest value like change 30 yields a number that's much larger than it should be.
I don't see a way to fix this without a substantial algorithm rework. Traditional dynamic-programming solutions to this problem involve producing the solutions in a specific order so you can avoid double-counting. i.e., they first decide how many dimes to use (here, 0 or 1), then compute how to make change for the remaining amounts without using any dimes. I don't know how to work that idea in here - your cache key would need to be larger, including both the target amount and also the allowed set of coins.

Why can't you use 'Just' syntax without 'let..in' block in Haskell?

I have a few questions regarding the Just syntax in Haskell.
When question arose when I was experimenting with different ways to write a function to calculate binomial coefficients.
Consider the function:
binom :: Integer -> Integer -> Maybe Integer
binom n k | n < k = Nothing
binom n k | k == 0 = Just 1
binom n k | n == k = Just 1
binom n k | otherwise = let
Just x = (binom (n-1) (k-1))
Just y = (binom (n-1) k)
in
Just (x + y)
When I try to write the otherwise case without the let..in block without the let..in block like so:
binom n k | otherwise = (binom (n-1) (k-1)) + (binom (n-1) k)
I am faced with a compilation error No instance for (Num (Maybe Integer)) arising from a use of ‘+’. And so my first thought was that I was forgetting the Just syntax so I rewrote it as
binom n k | otherwise = Just ((binom (n-1) (k-1)) + (binom (n-1) k))
I am faced with an error even more confusing:
Couldn't match type ‘Maybe Integer’ with ‘Integer’
Expected: Maybe Integer
Actual: Maybe (Maybe Integer)
If I add Just before the binom calls, the error just compounds:
Couldn't match type ‘Maybe (Maybe Integer)’ with ‘Integer’
Expected: Maybe Integer
Actual: Maybe (Maybe (Maybe Integer))
Furthermore, if I write:
Just x = binom 3 2
y = binom 3 2
x will have the value 3 and y will have the value Just 3.
So my questions are:
Why does the syntax requite the let..in block to compile properly?
In the function, why does Just add the Maybe type when I don't use let..in?
Contrarily, why does using Just outside of the function remove the Just from the value if it's type is Just :: a -> Maybe a
Bonus question, but unrelated:
When I declare the function without the type the compiler infers the type binom :: (Ord a1, Num a2, Num a1) => a1 -> a1 -> Maybe a2. Now I mostly understand what is happening here, but I don't see why a1 has two types.
Your question demonstrates a few ways you may have got confused about what is going on.
Firstly, Just is not any kind of syntax - it's just a data constructor (and therefore also a function) provided by the standard library. The reasons your failing attempts didn't compile are therefore not due to any syntax mishaps (the compiler would report a "parse error" in this case), but - as it actually reports - type errors. In other words the compiler is able to parse the code to make sense of it, but then when checking the types, realises something is up.
So to expand on your failing attempts, #1 was this:
binom n k | otherwise = Just ((binom (n-1) (k-1)) + (binom (n-1) k))
for which the reported error was
No instance for (Num (Maybe Integer)) arising from a use of ‘+’
This is because you were trying to add the results of 2 calls to binom - which according to your type declaration, are values of type Maybe Integer. And Haskell doesn't by default know how to add two Maybe Integer values (what would Just 2 + Nothing be?), so this doesn't work. You would need to - as you eventually do with your successful attempt - unwrap the underlying Integer values (assuming they exist! I'll come back to this later), add those up, and then wrap the resulting sum in a Just.
I won't dwell on the other failing attempts, but hopefully you can see that, in various ways, the types also fail to match up here too, in the ways described by the compiler. In Haskell you really have to understand the types, and just flinging various bits of syntax and function calls about in the wild hope that the thing will finally compile is a recipe for frustration and lack of success!
So to your explicit questions:
Why does the syntax requite the let..in block to compile properly?
It doesn't. It just needs the types to match everywhere. The version you ended up with:
let
Just x = (binom (n-1) (k-1))
Just y = (binom (n-1) k)
in
Just (x + y)
is fine (from the type-checking point of view, anyway!) because you're doing as I previously described - extracting the underlying values from the Just wrapper (these are x and y), adding them up and rewrapping them.
But this approach is flawed. For one thing, it's boilerplate - a lot of code to write and try to understand if you're seeing it for the first time, when the underlying pattern is really simple: "unwrap the values, add them together, then rewrap". So there should be a simpler, more understandable, way to do this. And there is, using the methods of the Applicative typeclass - of which the Maybe type is a member.
Experienced Haskellers would write the above in one of two ways. Either:
binom n k | otherwise = liftA2 (+) (binom (n-1) (k-1)) (binom (n-1) k)
or
binom n k | otherwise = (+) <$> binom (n-1) (k-1) <*> binom (n-1) k
(the latter being in what is called the "applicative style" - if you're unfamiliar with Applicative functors there's a great introduction in Learn You a Haskell here. )
And there's another advantage of doing this compared to your way, besides the avoidance of boilerplate code. Your pattern matches in the let... in expression assume that the results of binom (n-1) (k-1) and so on are of the form Just x. But they could also be Nothing - in which case your program will crash at runtime! And exactly this will indeed happen in your case, as #chepner describes in his answer.
Using liftA2 or <*> will, due to how the Applicative instance is implemented for Maybe, avoid a crash by simply giving you Nothing as soon as one of the things you're trying to add is Nothing. (And this in turn means your function will always return Nothing - I'll leave it to you to figure out how to fix it!)
I'm not sure I really understand your questions #2 and #3, so I won't address those directly - but I hope this has given you some increased understanding of how to work with Maybe in Haskell. Finally for your last question, although it's quite unrelated: "I don't see why a1 has two types" - it doesn't. a1 denotes a single type, because it's a single type variable. You're presumably referring to the fact it has two constraints - here Ord a1 and Num a1. Ord and Num here are typeclasses - like Applicative is that I mentioned earlier (albeit Ord and Num are simpler typeclasses). If you don't know what a typeclass is I recommend reading an introductory source, like Learn You a Haskell, before continuing much further with the language - but in short it's a bit like an interface, saying that the type must implement certain functions. Concretely, Ord says the type must implement order comparisons - you need that here because you've used the < operator - while Num says you can do numeric things with it, like addition. So that type signature just makes explicit what is implicit in your function definition - the values you use this function on must be of a type that implements both order comparison and numeric operations.
binom n k | otherwise = (binom (n-1) (k-1)) + (binom (n-1) k)
You can't add two Maybe values, but you can make use of the Functor instance to add the values already wrapped in Just.
binom n k | otherwise = fmap (+) (binom (n-1) (k-1)) (binom (n-1) k)
This doesn't quite work, as eventually the recursive calls will return Nothing, and fmap (+) x y == Nothing if either x or y is Nothing. The solution is to treat two different occurrences of n < k differently.
An "initial" use can return Nothing
A "recursive" use can simply return 0, since x + 0 == x.
binom will be implemented in terms of a helper that is guaranteed to receive arguments such that n >= k.
binom :: Integer -> Integer -> Maybe Integer
binom n k | n < k = Nothing
| otherwise = Just (binom' n k)
where binom' n 0 = 1
binom' n k | n == k = 1
| otherwise = binom' (n-1) (k-1) + binom' (n-1) k
This question has received excellent answers. However, I think it is worth mentioning that you can also use a monadic do construct, like the one normally used for the “main program” of a Haskell application.
The main program generally uses a do construct within the IO monad. Here, you would use a do construct within the Maybe monad.
Your binom function can be modified like this:
binom :: Integer -> Integer -> Maybe Integer
binom n k | n < 0 = Nothing -- added for completeness
binom n k | k < 0 = Nothing -- added for completeness
binom n k | n < k = Nothing
binom n k | k == 0 = Just 1
binom n k | n == k = Just 1
binom n k | otherwise = do -- monadic do construct, within the Maybe monad
x <- (binom (n-1) (k-1))
y <- (binom (n-1) k)
return (x+y)
main :: IO ()
main = do -- classic monadic do construct, within the IO monad
putStrLn "Hello impure world !"
putStrLn $ show (binom 6 3)
If a single <- extractor fails, the whole result is Nothing.
Please recall that in that context, return is just an ordinary function, with type signature:
return :: Monad m => a -> m a
Unlike in most imperative languages, return is not a keyword, and is not part of control flow.
A key concern is that if you have many quantities that can become Nothing, the do construct looks more scalable, that is, it can become more readable than pattern matching or lift'ing functions. More details about using the Maybe monad in the online Real World Haskell book.
Note that the Haskell library provides not only liftA2, as mentioned in Robin Zigmond's answer, but also other lift'ing functions up to lift6.
Interactive testing:
You can test the thing under the ghci interpreter, like this:
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
λ>
λ> do { n1 <- (Just 3) ; n2 <- (Just 42); return (n1+n2) ; }
Just 45
λ>
λ> do { n1 <- (Just 3) ; n2 <- (Just 42); n3 <- Nothing ; return (n1+n2+n3) ; }
Nothing
λ>
The exact semantics depend on the sort of monad involved. If you use the list monad, you get a Cartesian product of the lists you're extracting from:
λ>
λ> do { n1 <- [1,2,3] ; n2 <- [7,8,9]; return (n1,n2) ; }
[(1,7),(1,8),(1,9),(2,7),(2,8),(2,9),(3,7),(3,8),(3,9)]
λ>

How can I replace generators if I need only one result?

I'm playing with Haskell for first time.
I've created function that returns first precise enough result. It works as expected, but I'm using generator for this. How can I replace generator in this task?
integrateWithPrecision precision =
(take 1 $ preciseIntegrals precision) !! 0
preciseIntegrals :: Double -> [Double]
preciseIntegrals precision =
[
integrate (2 ^ power) pi | power <- [0..],
enoughPowerForPrecision power precision
]
You can use the beautiful until function. Here it is:
-- | #'until' p f# yields the result of applying #f# until #p# holds.
until :: (a -> Bool) -> (a -> a) -> a -> a
until p f x | p x = x
| otherwise = until p f (f x)
So, you can write your function like this:
integrateWithPrecision precision = integrate (2 ^ pow) pi
where
pow = until done succ 0
done pow = enoughPowerForPrecision pow precision
In your case, you do all the iteration and then compute a result just once. But until is useful even when you need to compute a result at each step - just use an (iter, result) tuple and then just extract the result at the end with snd.
It seems like you want to check higher and higher powers until you get one that satisfies a requirement. This is what you could do: First you define a function to get enough power, and then you integrate using that.
find gets the first element of a list that satisfies a condition – like being enough of a power! Then we need a fromJust to get the actual value from that. Please note that almost always, fromJust is a terrible idea to have in your code. However, in this case the list is infinite, so we will have troubles with infinite loops long before fromJust is able to crash the program.
enoughPower :: Double -> Int
enoughPower precision =
fromJust $ find (flip enoughPowerForPrecision precision) [0..]
preciseIntegrals :: Double -> Double
preciseIntegrals precision = integrate (2^(enoughPower precision)) pi
The function
\xs -> take 1 xs !! 0
is called head
head [] = error "Cannot take head of empty list"
head (x:xs) = x
Its use is somewhat unsafe, as shown it can throw an error if you pass it an empty list, but in this case since you can be certain your list is non-empty it's fine.
Also, we tend not to call these "generators" in Haskell as they're not a special form but are instead a simple consequence of lazy evaluation. In this case, preciseIntegrals is called a "list comprehension" and [0..] is nothing more than a lazily generated list.

Project euler problem 3 in haskell

I'm new in Haskell and try to solve 3 problem from http://projecteuler.net/.
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143 ?
My solution:
import Data.List
getD :: Int -> Int
getD x =
-- find deviders
let deriveList = filter (\y -> (x `mod` y) == 0) [1 .. x]
filteredList = filter isSimpleNumber deriveList
in maximum filteredList
-- Check is nmber simple
isSimpleNumber :: Int -> Bool
isSimpleNumber x = let deriveList = map (\y -> (x `mod` y)) [1 .. x]
filterLength = length ( filter (\z -> z == 0) deriveList)
in
case filterLength of
2 -> True
_ -> False
I try to run for example:
getD 13195
> 29
But when i try:
getD 600851475143
I get error Exception: Prelude.maximum: empty list Why?
Thank you #Barry Brown, I think i must use:
getD :: Integer -> Integer
But i get error:
Couldn't match expected type `Int' with actual type `Integer'
Expected type: [Int]
Actual type: [Integer]
In the second argument of `filter', namely `deriveList'
In the expression: filter isSimpleNumber deriveList
Thank you.
Your type signature limits the integer values to about 2^29. Try changing Int to Integer.
Edit:
I see that you already realised that you need to use Integer instead of Int. You need to change the types of both getD and isSimpleNumber otherwise you will get a type mismatch.
Also in general, if you are having trouble with types, simply remove the type declarations and let Haskell tell you the correct types.
Main> :t getD
getD :: Integral a => a -> a
Main> :t isSimpleNumber
isSimpleNumber :: Integral a => a -> Bool
After you found the error, may I point out that your solution is quite verbose? In this case a very simple implementation using brute force is good enough:
getD n = getD' n 2 where
getD' n f | n == f = f
| n `mod` f == 0 = getD' (n `div` f) f
| otherwise = getD' n (succ f)
this question is easy enough for brute-force solution, but it is a bad idea to do so because the whole idea of project euler is problems you need to really think of to solve (see end of answer)
so here are some of your program's flaws:
first, use rem instead of mod. it is more efficient.
some mathematical thinking should have told you that you don't need to check all numbers from 1 to x in the isprime function and the getD function, but checking all numbers from the squareroot to one (or reversed) should be sufficient. note that in getD you will actually need to filter numbers between x and the square root, because you search for the biggest one.
why do you use the maximum function in getD? you know the list is monotonically growing, so you may as well get the last one.
despite you only need the biggest divisor (which is prime) you compute the divisors list from small to big making the computer check for each value if it is a divisor or not although discarding the result once a bigger divisor is found. it should be fixed by filtering the list of numbers from x to 1, not from 1 to x. this will cause the computer to check divisibility (how should I say that?) for the biggest possible divisor, not throwing to the trash the knowledge of previous checks. note that this optimization takes effect only if the previous point is optimized, because otherwise the computer will compute all divisors anyway.
with the previous points mixed, you should have filtered all numbers [x,x-1 .. squareroot x] and taken the first.
you don't use an efficient isPrime function. if I were you, I would have searched for an isprime library function, which is guaranteed to be efficient.
and there are more..
with this kind of code you will never be able to solve harder project euler problems. they are designed to need extra thinking about the problem (for instance noticing you don't have to check numbers greater from the square root) and writing fast and efficient code. this is the purpose of project euler; being smart about programming. so don't skip it.

How do I use fix, and how does it work?

I was a bit confused by the documentation for fix (although I think I understand what it's supposed to do now), so I looked at the source code. That left me more confused:
fix :: (a -> a) -> a
fix f = let x = f x in x
How exactly does this return a fixed point?
I decided to try it out at the command line:
Prelude Data.Function> fix id
...
And it hangs there. Now to be fair, this is on my old macbook which is kind of slow. However, this function can't be too computationally expensive since anything passed in to id gives that same thing back (not to mention that it's eating up no CPU time). What am I doing wrong?
You are doing nothing wrong. fix id is an infinite loop.
When we say that fix returns the least fixed point of a function, we mean that in the domain theory sense. So fix (\x -> 2*x-1) is not going to return 1, because although 1 is a fixed point of that function, it is not the least one in the domain ordering.
I can't describe the domain ordering in a mere paragraph or two, so I will refer you to the domain theory link above. It is an excellent tutorial, easy to read, and quite enlightening. I highly recommend it.
For the view from 10,000 feet, fix is a higher-order function which encodes the idea of recursion. If you have the expression:
let x = 1:x in x
Which results in the infinite list [1,1..], you could say the same thing using fix:
fix (\x -> 1:x)
(Or simply fix (1:)), which says find me a fixed point of the (1:) function, IOW a value x such that x = 1:x... just like we defined above. As you can see from the definition, fix is nothing more than this idea -- recursion encapsulated into a function.
It is a truly general concept of recursion as well -- you can write any recursive function this way, including functions that use polymorphic recursion. So for example the typical fibonacci function:
fib n = if n < 2 then n else fib (n-1) + fib (n-2)
Can be written using fix this way:
fib = fix (\f -> \n -> if n < 2 then n else f (n-1) + f (n-2))
Exercise: expand the definition of fix to show that these two definitions of fib are equivalent.
But for a full understanding, read about domain theory. It's really cool stuff.
I don't claim to understand this at all, but if this helps anyone...then yippee.
Consider the definition of fix. fix f = let x = f x in x. The mind-boggling part is that x is defined as f x. But think about it for a minute.
x = f x
Since x = f x, then we can substitute the value of x on the right hand side of that, right? So therefore...
x = f . f $ x -- or x = f (f x)
x = f . f . f $ x -- or x = f (f (f x))
x = f . f . f . f . f . f . f . f . f . f . f $ x -- etc.
So the trick is, in order to terminate, f has to generate some sort of structure, so that a later f can pattern match that structure and terminate the recursion, without actually caring about the full "value" of its parameter (?)
Unless, of course, you want to do something like create an infinite list, as luqui illustrated.
TomMD's factorial explanation is good. Fix's type signature is (a -> a) -> a. The type signature for (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) is (b -> b) -> b -> b, in other words, (b -> b) -> (b -> b). So we can say that a = (b -> b). That way, fix takes our function, which is a -> a, or really, (b -> b) -> (b -> b), and will return a result of type a, in other words, b -> b, in other words, another function!
Wait, I thought it was supposed to return a fixed point...not a function. Well it does, sort of (since functions are data). You can imagine that it gave us the definitive function for finding a factorial. We gave it a function that dind't know how to recurse (hence one of the parameters to it is a function used to recurse), and fix taught it how to recurse.
Remember how I said that f has to generate some sort of structure so that a later f can pattern match and terminate? Well that's not exactly right, I guess. TomMD illustrated how we can expand x to apply the function and step towards the base case. For his function, he used an if/then, and that is what causes termination. After repeated replacements, the in part of the whole definition of fix eventually stops being defined in terms of x and that is when it is computable and complete.
You need a way for the fixpoint to terminate. Expanding your example it's obvious it won't finish:
fix id
--> let x = id x in x
--> id x
--> id (id x)
--> id (id (id x))
--> ...
Here is a real example of me using fix (note I don't use fix often and was probably tired / not worrying about readable code when I wrote this):
(fix (\f h -> if (pred h) then f (mutate h) else h)) q
WTF, you say! Well, yes, but there are a few really useful points here. First of all, your first fix argument should usually be a function which is the 'recurse' case and the second argument is the data on which to act. Here is the same code as a named function:
getQ h
| pred h = getQ (mutate h)
| otherwise = h
If you're still confused then perhaps factorial will be an easier example:
fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 5 -->* 120
Notice the evaluation:
fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 3 -->
let x = (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x in x 3 -->
let x = ... in (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 3 -->
let x = ... in (\d -> if d > 0 then d * (x (d-1)) else 1) 3
Oh, did you just see that? That x became a function inside our then branch.
let x = ... in if 3 > 0 then 3 * (x (3 - 1)) else 1) -->
let x = ... in 3 * x 2 -->
let x = ... in 3 * (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 2 -->
In the above you need to remember x = f x, hence the two arguments of x 2 at the end instead of just 2.
let x = ... in 3 * (\d -> if d > 0 then d * (x (d-1)) else 1) 2 -->
And I'll stop here!
How I understand it is, it finds a value for the function, such that it outputs the same thing you give it. The catch is, it will always choose undefined (or an infinite loop, in haskell, undefined and infinite loops are the same) or whatever has the most undefineds in it. For example, with id,
λ <*Main Data.Function>: id undefined
*** Exception: Prelude.undefined
As you can see, undefined is a fixed point, so fix will pick that. If you instead do (\x->1:x).
λ <*Main Data.Function>: undefined
*** Exception: Prelude.undefined
λ <*Main Data.Function>: (\x->1:x) undefined
[1*** Exception: Prelude.undefined
So fix can't pick undefined. To make it a bit more connected to infinite loops.
λ <*Main Data.Function>: let y=y in y
^CInterrupted.
λ <*Main Data.Function>: (\x->1:x) (let y=y in y)
[1^CInterrupted.
Again, a slight difference. So what is the fixed point? Let us try repeat 1.
λ <*Main Data.Function>: repeat 1
[1,1,1,1,1,1, and so on
λ <*Main Data.Function>: (\x->1:x) $ repeat 1
[1,1,1,1,1,1, and so on
It is the same! Since this is the only fixed point, fix must settle on it. Sorry fix, no infinite loops or undefined for you.
As others pointed out, fix somehow captures the essence of recursion. Other answers already do a great job at explaining how fix works. So let's take a look from another angle and see how fix can be derived by generalising, starting from a specific problem: we want to implement the factorial function.
Let's first define a non recursive factorial function. We will use it later to "bootstrap" our recursive implementation.
factorial n = product [1..n]
We approximate the factorial function by a sequence of functions: for each natural number n, factorial_n coincides with factorial up to and including n. Clearly factorial_n converges towards factorial for n going towards infinity.
factorial_0 n = if n == 0 then 1 else undefined
factorial_1 n = n * factorial_0(n - 1)
factorial_2 n = n * factorial_1(n - 1)
factorial_3 n = n * factorial_2(n - 1)
...
Instead of writing factorial_n out by hand for every n, we implement a factory function that creates these for us. We do this by factoring the commonalities out and abstracting over the calls to factorial_[n - 1] by making them a parameter to the factory function.
factorialMaker f n = if n == 0 then 1 else n * f(n - 1)
Using this factory, we can create the same converging sequence of functions as above. For each factorial_n we need to pass a function that calculates the factorials up to n - 1. We simply use the factorial_[n - 1] from the previous step.
factorial_0 = factorialMaker undefined
factorial_1 = factorialMaker factorial_0
factorial_2 = factorialMaker factorial_1
factorial_3 = factorialMaker factorial_2
...
If we pass our real factorial function instead, we materialize the limit of the series.
factorial_inf = factorialMaker factorial
But since that limit is the real factorial function we have factorial = factorial_inf and thus
factorial = factorialMaker factorial
Which means that factorial is a fixed-point of factorialMaker.
Finally we derive the function fix, which returns factorial given factorialMaker. We do this by abstracting over factorialMaker and make it an argument to fix. (i.e. f corresponds to factorialMaker and fix f to factorial):
fix f = f (fix f)
Now we find factorial by calculating the fixed-point of factorialMaker.
factorial = fix factorialMaker

Resources