Related
First of all, I want to say that I'm very very inexperienced with Haskell, and I know that I have done something (or multiple things) terribly wrong, been struggling for hours but I can't seem to find it.
power :: Int -> Int -> Int
power x y | y == 0 = 1
| x == 0 = 0
list = replicate y x
foldr (*) x list
main = print $ power 3 5
Error most of the time is either x and y not being passed to the replicate function or that foldr is a naked function, I understand what they both mean but have no idea on how I can pass the variables or come up with a solution.
You here created four functions: power, list, foldr and main. But you use variables x and y in the definition of the list function.
You can work with a where clause to specify subexpressions, for example:
power :: Int -> Int -> Int
power x y | y == 0 = 1
| x == 0 = 0
| otherwise = foldr (*) 1 list
where list = replicate y x
or perhaps more elegant with pattern matching:
power :: Int -> Int -> Int
power 0 _ = 0
power x y = foldr (*) 1 (replicate y x)
main = print $ power 3 5
Here we can also eliminate the case for x0, since our foldr starts working with 1, not x.
This algorithm is however not very efficient, since it is linear in the value of y. By checking recursively if the exponent is even or odd, you can make it faster. I leave this as an exercise.
You were very close! The main things that need to be fixed are:
When writing a definition with guards, the “fallback” case needs to be a guard as well, conventionally written with otherwise.
Recall that a definition without guards looks like this, with one left side (a name and parameter patterns/names) and one right side (an expression):
name patterns = expression
With guard conditions, there is one right-hand side for each guard:
name patterns | condition1 = expression1
| condition2 = expression2
…
| otherwise = expressionn
otherwise is really just an alias for True, that is, such a guard always matches. The only thing special about otherwise is that the compiler uses it as a hint when analysing whether a pattern match covers all possible cases.
In order to define a variable list, local to the definition of power, using the parameters x and y, you need to use either a let…in… expression, that is, let block in expression, or a where clause, equation where block. A block is a series of items (in this case, local definitions) which must all be written starting at the same column of indentation, or be delimited by explicit curly braces {…} and semicolons ;.
Using let…in… follows the structure of your original code pretty closely. I will adjust the indentation style to avoid needing to align anything, by putting a newline and a constant amount of indentation instead.
power :: Int -> Int -> Int
power x y
| y == 0 = 1
| x == 0 = 0
| otherwise = let
list = replicate y x
in foldr (*) x list
main :: IO ()
main = print $ power 3 5
Attaching a where clause to an equation is slightly more common than using a let…in… expression on the right side of an equation.
power :: Int -> Int -> Int
power x y
| y == 0 = 1
| x == 0 = 0
| otherwise = foldr (*) x list
where
list = replicate y x
main :: IO ()
main = print $ power 3 5
Note that in this case, there is a slight difference: the variable list is visible in all of the right-hand sides, although we only use it in one of them. With let list = … in e, list is only defined within e. In general, it’s helpful for readability to keep the scope of a variable as small as possible, although you can certainly go overboard:
a = …
where
b = …
where
c = …
where
d = …
-- If you see this much nesting, rethink!
If you run into issues with alignment and indentation, you can always use explicit delimiters instead. The code I wrote is equivalent to the following.
power :: Int -> Int -> Int; -- Begin ‘power’ signature.
power x y
| y == 0 = 1
| x == 0 = 0
| otherwise = let { -- Begin ‘let’ block.
list = replicate y x; -- End ‘list’ equation.
} in foldr (*) x list; -- End ‘let’ block, then end ‘power’ equation.
main :: IO (); -- Begin ‘main’ signature.
main = print $ power 3 5; -- End ‘main’ equation.
Or similarly with where { … }.
I have this code that will return the index of a char in a char array but I want my function to return something like -1 if the value isn't in the array. As it stands the function returns the size of the array if the element isn't in the array. Any ideas on how to change my code in order to apply this feature?
I am trying not to use any fancy functions to do this. I just want simple code without built-in functions.
isPartOf :: [(Char)] -> (Char) -> Int
isPartOf [] a = 0
isPartOf (a:b) c
| a == c = 0
| otherwise = 1 + isPartOf b c
For example:
*Main> isPartOf [('a'),('b'),('c')] ('z')
3
But I want:
*Main> isPartOf [('a'),('b'),('c')] ('z')
-1
Let's try to define such a function, but instead of returning -1 in case of element being not a part of the list, we can return Nothing:
isPartOf :: Eq a => [a] -> a -> Maybe Int
isPartOf [] _ = Nothing
isPartOf (x : xs) a | x == a = Just 0
| otherwise = fmap ((+) 1) (isPartOf xs a)
So, it works like that:
>> isPartOf [('a'),('b'),('c')] ('z')
Nothing
it :: Maybe Int
>> isPartOf [('a'),('b'),('c')] ('c')
Just 2
it :: Maybe Int
After that we can use built-in function fromMaybe to convert the Nothing case to -1:
>> fromMaybe (-1) $ isPartOf [('a'),('b'),('c')] ('c')
2
it :: Int
>> fromMaybe (-1) $ isPartOf [('a'),('b'),('c')] ('z')
-1
it :: Int
In case you're curios if such a function already exist, you can use Hoogle for that, searching the [a] -> a -> Maybe Int function: https://www.haskell.org/hoogle/?hoogle=%5Ba%5D+-%3E+a+-%3E+Maybe+Int
And the first answer will be elemIndex:
>> elemIndex 'c' [('a'),('b'),('c')]
Just 2
it :: Maybe Int
>> elemIndex 'z' [('a'),('b'),('c')]
Nothing
it :: Maybe Int
Hope this helps.
The smallest change to achieve this is
isPartOf :: [Char] -> Char -> Int
isPartOf [] a = (-1) -- was: 0
isPartOf (a:b) c
| a == c = 0
| otherwise = 1 + -- was: isPartOf b c
if (isPartOf b c) < 0 then (-2) else (isPartOf b c)
This is terrible computationally though. It recalculates the same value twice; what's worse is that the calculation is done with the recursive call and so the recursive call will be done twice and the time complexity overall will change from linear to exponential!
Let's not do that. But also, what's so special about Char? There's lots of stuff special about the Char but none are used here, except the comparison, (==).
The types the values of which can be compared by equality are known as those belonging to the Eq (for "equality") type class: Eq a => a. a is a type variable capable of assuming any type whatsoever; but here it is constrained to be such that ... yes, belongs to the Eq type class.
And so we write
isPartOf :: Eq a => [a] -> a -> Int
isPartOf [] a = (-1)
isPartOf (a:b) c
| a == c = 0
| otherwise = let d = isPartOf b c in
1 + if d < 0 then (-2) else d
That (-2) looks terribly ad-hoc! A more compact and idiomatic version using guards will also allow us to address this:
isPartOf :: Eq a => [a] -> a -> Int
isPartOf [] a = (-1)
isPartOf (a:b) c
| a == c = 0
| d < 0 = d
| otherwise = 1 + d
where
d = isPartOf b c
Yes, we can define d in the where clause, and use it in our guards, as well as in the body of each clause. Thanks to laziness it won't even be calculated once if its value wasn't needed, like in the first clause.
Now this code is passable.
The conditional passing and transformation is captured by the Maybe data type's Functor interface / instance:
fmap f Nothing = Nothing -- is not changed
fmap f (Just x) = Just (f x) -- is changed
which is what the other answer here is using. But it could be seen as "fancy" when we only start learning Haskell.
When you've written more functions like that, and become "fed up" with repeating the same pattern manually over and over, you'll come to appreciate it and will want to use it. But only then.
Yet another concern is that our code calculates its result on the way back from the recursion's base case.
But it could instead calculate it on the way forward, towards it, so it can return it immediately when the matching character is found. And if the end of list is found, discard the result calculated so far, and return (-1) instead. This is the approach taken by the second answer.
Though creating an additional function litters the global name space. It is usual to do this by defining it internally, in the so called "worker/wrapper" transformation:
isPartOf :: Eq a => [a] -> a -> Int
isPartOf xs c = go xs 0
where
go [] i = (-1)
go (a:b) i
| a == c = i
| otherwise = -- go b (1 + i)
go b $! (1 + i)
Additional boon is that we don't need to pass around the unchanged value c -- it is available in the outer scope, from the point of view of the internal "worker" function go, "wrapped" by and accessible only to our function, isPartOf.
$! is a special call operator which ensures that its argument value is calculated right away, and not delayed. This eliminates an unwanted (in this case) laziness and improves the code efficiency even more.
But from the point of view of overall cleanliness of the design it is better to return the index i wrapped in a Maybe (i.e. Just i or Nothing) instead of using a "special" value which is not so special after all -- it is still an Int.
It is good to have types reflect our intentions, and Maybe Int expresses it clearly and cleanly, so we don't have to remember which of the values are special and which regular, so that that knowledge is not external to our program text, but inherent to it.
It is a small and easy change, combining the best parts from the two previous variants:
isPartOf :: Eq a => [a] -> a -> Maybe Int
isPartOf .....
.......
....... Nothing .....
.......
....... Just i .....
.......
(none of the code was tested. if there are errors, you're invited to find them and correct them, and validate it by testing).
You can achieve it easily if you just pass current element idx to the next recursion:
isPartOf :: [Char] -> Char -> Int
isPartOf lst c = isPartOf' lst c 0
isPartOf' :: [Char] -> Char -> Int -> Int
isPartOf' [] a _ = -1
isPartOf' (a:b) c idx
| a == c = idx
| otherwise = isPartOf' b c (idx + 1)
You are using your function as an accumulator. This is cool except the additions with negative one. An accumulator cannot switch from accumulating to providing a negative 1. You want two different things from your function accumulator. You can use a counter for one thing then if the count becomes unnecessary because no match is found and a negative 1 is issued and nothing is lost. The count would be yet another parameter. ugh. You can use Maybe but that complicates. Two functions, like above is simpler. Here are two functions. The first is yours but the accumulator is not additive it's concatenative.
cIn (x:xs) c | x == c = [1]
| null xs = [-1]
| otherwise = 1:cIn xs c
Cin ['a','b','c'] 'c'
[1,1,1]
cIn ['a','b','c'] 'x'
[1,1,-1]
So the second function is
f ls = if last ls == 1 then sum ls else -1
It will
f $ Cin ['a','b','c'] 'c'
3
and
f $ Cin ['a','b','c'] 'x'
-1
You can zero the index base by changing [1] to [0]
Apologies for my poor wording of the question. I've tried searching for an answer but not knowing what to search is making it very difficult to find one.
Here is a simple function which calculates the area of a triangle.
triangleArea :: Float -> Float -> Float -> Float
triangleArea a b c
| (a + b) <= c = error "Not a triangle!"
| (a + c) <= b = error "Not a triangle!"
| (b + c) <= a = error "Not a triangle!"
| otherwise = sqrt (s * (s - a) * (s - b) * (s - c))
where s = (a + b + c) / 2
Three lines of the function have been taken up for the purposes of error checking. I was wondering if these three lines could be condensed into one generic line.
I was wondering if something similar to the following would be possible
(arg1 + arg2) == arg3
where Haskell knows to check each possible combination of the three arguments.
I think #behzad.nouri's comment is the best. Sometimes doing a little math is the best way to program. Here's a somewhat overdone expansion on #melpomene's solution, which I thought would be fun to share. Let's write a function similar to permutations but that computes combinations:
import Control.Arrow (first, second)
-- choose n xs returns a list of tuples, the first component of each having
-- n elements and the second component having the rest, in all combinations
-- (ignoring order within the lists). N.B. this would be faster if implemented
-- using a DList.
choose :: Int -> [a] -> [([a],[a])]
choose 0 xs = [([], xs)]
choose _ [] = []
choose n (x:xs) =
map (first (x:)) (choose (n-1) xs) ++
map (second (x:)) (choose n xs)
So..
ghci> choose 2 [1,2,3]
[([1,2],[3]),([1,3],[2]),([2,3],[1])]
Now you can write
triangleArea a b c
| or [ x + y <= z | ([x,y], [z]) <- choose 2 [a,b,c] ] = error ...
This doesn't address the question of how to shorten your error checking code, but you may be able to limit how often you repeat it by defining some new types with invariants. This function needs error checking because you can't trust the user to supply Float triples that make a reasonable triangle, and if you continue to define functions this way then every triangle-related function you write would need similar error checks.
However, if you define a Triangle type, you can check your invariants only once, when a triangle is created, and then all other functions will be guaranteed to receive valid triangles:
module Triangle (Triangle(), mkTriangle, area) where
data Triangle a = Triangle a a a deriving Show
mkTriangle :: (Num a, Ord a) => a -> a -> a -> Either String (Triangle a)
mkTriangle a b c
| a + b <= c = wrong
| a + c <= b = wrong
| b + c <= a = wrong
| otherwise = Right $ Triangle a b c
where wrong = Left "Not a triangle!"
area :: Floating a => Triangle a -> a
area (Triangle a b c) = sqrt (s * (s - a) * (s - b) * (s - c))
where s = (a + b + c) / 2
Here we export the Triangle type, but not its constructor, so that the client must use mkTriangle instead, which can do the required error checking. Then area, and any other triangle functions you write, can omit the checks that they are receiving a valid triangle. This general pattern is called "smart constructors".
Here are two ideas.
Using existing tools, you can generate all the permutations of the arguments and check that they all satisfy a condition. Thus:
import Data.List
triangleArea a b c
| any (\[x, y, z] -> x + y <= z) (permutations [a,b,c])
= error "Not a triangle!"
| otherwise = {- ... -}
This doesn't require writing very much additional code; however, it will search some permutations you don't care about.
Use the usual trick for choosing an element from a list and the left-overs. The zippers function is one I use frequently:
zippers :: [a] -> [([a], a, [a])]
zippers = go [] where
go b [] = []
go b (v:e) = (b, v, e) : go (v:b) e
We can use it to build a function which chooses only appropriate triples of elements:
triples :: [a] -> [(a, a, a)]
triples xs = do
(b1, v1, e1) <- zippers xs
(b2, v2, e2) <- zippers e1
v3 <- b1 ++ b2 ++ e2
return (v1, v2, v3)
Now we can write our guard like in part (1), but it will only consider unique pairings for the addition.
triangleArea a b c
| any (\(x, y, z) -> x + y <= z) (triples [a,b,c])
= error "Not a triangle!"
| otherwise = {- ... -}
Consider the modified Euler problem #4 -- "Find the maximum palindromic number which is a product of two numbers between 100 and 9999."
rev :: Int -> Int
rev x = rev' x 0
rev' :: Int -> Int -> Int
rev' n r
| n == 0 = r
| otherwise = rev' (n `div` 10) (r * 10 + n `mod` 10)
pali :: Int -> Bool
pali x = x == rev x
main :: IO ()
main = print . maximum $ [ x*y | x <- nums, y <- nums, pali (x*y)]
where
nums = [9999,9998..100]
This Haskell solution using -O2 and ghc 7.4.1 takes about 18
seconds.
The similar C solution takes 0.1 second.
So Haskell is 180 times
slower. What's wrong with my solution? I assume that this type of
problems Haskell solves pretty well.
Appendix - analogue C solution:
#define A 100
#define B 9999
int ispali(int n)
{
int n0=n, k=0;
while (n>0) {
k = 10*k + n%10;
n /= 10;
}
return n0 == k;
}
int main(void)
{
int max = 0;
for (int i=B; i>=A; i--)
for (int j=B; j>=A; j--) {
if (i*j > max && ispali(i*j))
max = i*j; }
printf("%d\n", max);
}
The similar C solution
That is a common misconception.
Lists are not loops!
And using lists to emulate loops has performance implications unless the compiler is able to eliminate the list from the code.
If you want to compare apples to apples, write the Haskell structure more or less equivalent to a loop, a tail recursive worker (with strict accumulator, though often the compiler is smart enough to figure out the strictness by itself).
Now let's take a more detailed look. For comparison, the C, compiled with gcc -O3, takes ~0.08 seconds here, the original Haskell, compiled with ghc -O2 takes ~20.3 seconds, with ghc -O2 -fllvm ~19.9 seconds. Pretty terrible.
One mistake in the original code is to use div and mod. The C code uses the equivalent of quot and rem, which map to the machine division instructions and are faster than div and mod. For positive arguments, the semantics are the same, so whenever you know that the arguments are always non-negative, never use div and mod.
Changing that, the running time becomes ~15.4 seconds when compiling with the native code generator, and ~2.9 seconds when compiling with the LLVM backend.
The difference is due to the fact that even the machine division operations are quite slow, and LLVM replaces the division/remainder with a multiply-and-shift operation. Doing the same by hand for the native backend (actually, a slightly better replacement taking advantage of the fact that I know the arguments will always be non-negative) brings its time down to ~2.2 seconds.
We're getting closer, but are still a far cry from the C.
That is due to the lists. The code still builds a list of palindromes (and traverses a list of Ints for the two factors).
Since lists cannot contain unboxed elements, that means there is a lot of boxing and unboxing going on in the code, that takes time.
So let us eliminate the lists, and take a look at the result of translating the C to Haskell:
module Main (main) where
a :: Int
a = 100
b :: Int
b = 9999
ispali :: Int -> Bool
ispali n = go n 0
where
go 0 acc = acc == n
go m acc = go (m `quot` 10) (acc * 10 + (m `rem` 10))
maxpal :: Int
maxpal = go 0 b
where
go mx i
| i < a = mx
| otherwise = go (inner mx b) (i-1)
where
inner m j
| j < a = m
| p > m && ispali p = inner p (j-1)
| otherwise = inner m (j-1)
where
p = i*j
main :: IO ()
main = print maxpal
The nested loop is translated to two nested worker functions, we use an accumulator to store the largest palindrome found so far. Compiled with ghc -O2, that runs in ~0.18 seconds, with ghc -O2 -fllvm it runs in ~0.14 seconds (yes, LLVM is better at optimising loops than the native code generator).
Still not quite there, but a factor of about 2 isn't too bad.
Maybe some find the following where the loop is abstracted out more readable, the generated core is for all intents and purposes identical (modulo a switch of argument order), and the performance of course the same:
module Main (main) where
a :: Int
a = 100
b :: Int
b = 9999
ispali :: Int -> Bool
ispali n = go n 0
where
go 0 acc = acc == n
go m acc = go (m `quot` 10) (acc * 10 + (m `rem` 10))
downto :: Int -> Int -> a -> (a -> Int -> a) -> a
downto high low acc fun = go high acc
where
go i acc
| i < low = acc
| otherwise = go (i-1) (fun acc i)
maxpal :: Int
maxpal = downto b a 0 $ \m i ->
downto b a m $ \mx j ->
let p = i*j
in if mx < p && ispali p then p else mx
main :: IO ()
main = print maxpal
#axblount is at least partly right; the following modification makes the program run almost three times as fast as the original:
maxPalindrome = foldl f 0
where f a x | x > a && pali x = x
| otherwise = a
main :: IO ()
main = print . maxPalindrome $ [x * y | x <- nums, y <- nums]
where nums = [9999,9998..100]
That still leaves a factor 60 slowdown, though.
This is more true to what the C code is doing:
maxpali :: [Int] -> Int
maxpali xs = go xs 0
where
go [] m = m
go (x:xs) m = if x > m && pali(x) then go xs x else go xs m
main :: IO()
main = print . maxpali $ [ x*y | x <- nums, y <- nums ]
where nums = [9999,9998..100]
On my box this takes 2 seconds vs .5 for the C version.
Haskell may be storing that entire list [ x*y | x <- nums, y <- nums, pali (x*y)] where as the C solution calculates the maximum on the fly. I'm not sure about this.
Also the C solution will only calculate ispali if the product beats the previous maximum. I would bet Haskell calculates are palindrome products regardless of whether x*y is a possible max.
It seems to me that you are having a branch prediction problem. In the C code, you have two nested loops and as soon as a palindrome is seen in the inner loop, the rest of the inner loop will be skipped very fast.
The way you feed this list of products instead of the nested loops I am not sure that ghc is doing any of this prediction.
Another way to write this is to use two folds, instead of one fold over the flattened list:
-- foldl g0 0 [x*y | x<-[b-1,b-2..a], y<-[b-1,b-2..a], pali(x*y)] (A)
-- foldl g1 0 [x*y | x<-[b-1,b-2..a], y<-[b-1,b-2..a]] (B)
-- foldl g2 0 [ [x*y | y<-[b-1,b-2..a]] | x<-[b-1,b-2..a]] (C)
maxpal b a = foldl f1 0 [b-1,b-2..a] -- (D)
where
f1 m x = foldl f2 m [b-1,b-2..a]
where
f2 m y | p>m && pali p = p
| otherwise = m
where p = x*y
main = print $ maxpal 10000 100
Seems to run much faster than (B) (as in larsmans's answer), too (only 3x - 4x slower then the following loops-based code). Fusing foldl and enumFromThenTo definitions gets us the "functional loops" code (as in DanielFischer's answer),
maxpal_loops b a = f (b-1) 0 -- (E)
where
f x m | x < a = m
| otherwise = g (b-1) m
where
g y m | y < a = f (x-1) m
| p>m && pali p = g (y-1) p
| otherwise = g (y-1) m
where p = x*y
The (C) variant is very suggestive of further algorithmic improvements (that's outside the scope of the original Q of course) that exploit the hidden order in the lists, destroyed by the flattening:
{- foldl g2 0 [ [x*y | y<-[b-1,b-2..a]] | x<-[b-1,b-2..a]] (C)
foldl g2 0 [ [x*y | y<-[x, x-1..a]] | x<-[b-1,b-2..a]] (C1)
foldl g0 0 [ safehead 0 . filter pali $
[x*y | y<-[x, x-1..a]] | x<-[b-1,b-2..a]] (C2)
fst $ until ... (\(m,s)-> (max m .
safehead 0 . filter pali . takeWhile (> m) $
head s, tail s))
(0,[ [x*y | y<-[x, x-1..a]] | x<-[b-1,b-2..a]]) (C3)
safehead 0 $ filter pali $ mergeAllDescending
[ [x*y | y<-[x, x-1..a]] | x<-[b-1,b-2..a]] (C4)
-}
(C3) can stop as soon as the head x*y in a sub-list is smaller than the currently found maximum. It is what short-cutting functional loops code could achieve, but not (C4), which is guaranteed to find the maximal palindromic number first. Plus, for list-based code its algorithmic nature is more visually apparent, IMO.
I have the following beginning of a function, and am unsure as to how I should return Middle Number (i.e. the number that is neither the largest nor smallest):
middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
| ...
I would recommend you break the function into two steps: First, sort the three numbers. Then, take the middle element. For the first step, also consider if you can take it one step at a time; each step bringing it a bit closer to being fully sorted, then tail-recursing back to bring it even closer.
The "middle number" is larger than one of the numbers, but smaller than the other number. And there is only one middle number. The most mechanical way to solve this would be to start off
middleNumber a b c
| a < b && a > c = a
Check if a is the middle number by being less than b but greater than c.
Now what if a is the middle number, but it's actually greater than b and less than c? There's another guard. What if b is the middle number? There's another 2 guards. What if c is the middle number? There's 2 more guards, for a total of 6 different cases.
(btw, the expression | a < b && a > c = a is referred to as a guard. If you don't have a firm grasp yet of what guards are, then I recommend LYAH # Guards)
Of course there are better ways to write the function, but for understanding purposes it's good to be able to manually and systematically break down all of the possible situations, and determine what to do in each situation. How To Design Programs is a great book for learning how to be systematic in this way.
The obligatory Rube-Goldberg-answer:
import Control.Applicative
middleNumber a b c = sum $ [sum, negate.minimum, negate.maximum] <*> [[a,b,c]]
[Edit]
Here is another version:
middleNumber a b c = fst $ maximumBy (compare `on` abs.snd) [(a,b-c),(b,c-a),(c,a-b)]
I'm sure we could translate this to arrow syntax for further obfuscation, but I leave that task to the interested reader.
Expanding on Dan Burton's answer with guards, I evaluate a, b, c cases each in its own guard. However, what happens when 2 of the numbers are equal? Then the middle number should be one of the duplicated ones.
middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
| (a > b && a < c) || (a > c && a < b) = a
| (b > a && b < c) || (b > c && b < a) = b
| (c > a && c < b) || (c > b && c < a) = c
| otherwise = if a == b then a else c
I did a quick brute force method but this is most certainly not the best solution
import Data.List
middleNum :: Int -> Int -> Int -> Int
middleNum a b c = (\[_,m,_] -> m) $ sort $ a:b:c:[]
Obviously this is an awful idea as it explicitly relies on there being 3 items in the list, but it does the job
You can leverage guards and where to obtain the same result in simple way:
middleNumber :: Int -> Int -> Int -> Int
middleNumber x y z
| a == x = max y z
| a == y = max x z
| a == z = max x y
where
a = max x $ max y z
If you don't have access to the built-in max. you can easily write your own.
max' :: Int -> Int -> Int
max' x y
| x > y = x
| otherwise = y