The following function is pretty straightforward:
test :: Int -> Int
test x = case x of
0 -> 0
1 -> 1
_ -> 2
and indeed, test 0 == 0
, test 1 == 1
, and test 77 == 2
.
The following function is almost as straightforward:
import Data.Ratio
test2 :: Rational -> Int
test2 = case x of
0 -> 0
1 % 2 -> 1
_ -> 2
Loading this code in GHCi gives an error Parse error in pattern: 1 % 2
.
What gives? Why can't I pattern-match on rational numbers? I can solve the real-world problem this example came from with guards, but I'm curious why pattern-matching doesn't work.
You can in general not pattern match on functions. That would require computing the inverse, which usually doesn't even exist. You can only match on constructors like Just
or :+
: these are recognisable from ordinary functions / infix operators by starting with an uppercase character or a colon.
You can pattern match on rationals.
import GHC.Real (:%)
test2 :: Rational -> Int
test2 = case x of
0 -> 0
1 :% 2 -> 1
_ -> 2
The reason, I suppose, why it's not really recommended to use :%
(and it's hence only exported from an internal module, not from Data.Ratio
) is that Ratio
values are always supposed to be minimal, but :%
as a plain constructor doesn't ensure this:
Prelude Data.Ratio GHC.Real> 4%2
2 % 1
Prelude Data.Ratio GHC.Real> 4:%2
4 % 2
In particular, if you'd then actually pattern-match on such an unnormalised fraction, you couldn't be sure to succeed.
In cases like 1%2
, you can circumvent the problem by pattern matching on a decimal fraction (finite decimal fractions are unique):
test2 :: Rational -> Int
test2 = case x of
0 -> 0
0.5 -> 1
_ -> 2
Of course, this is perhaps not that nice. In modern Haskell, one could theoretically re-define :%
as a smart pattern synonym:
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Data.Ratio
numDenum :: Integral a => Ratio a -> (a,a)
numDenum x = (numerator x, denominator x)
pattern (:%) :: () => Integral a => a -> a -> Ratio a
pattern a:%b <- (numDenum -> (a,b))
where a:%b = a%b
which could then be used as in your original example.
... but frankly, it's probably better to just use numerator
and denominator
as they are.