Я подумал, что было бы неплохо разрешить произвольное цепное сравнение в Haskell, поэтому вы могли бы делать простые проверки диапазона, такие как:
x <= y < z
и более сложные вещи, такие как
x /= y < z == a
Где два вышеупомянутых семантически эквивалентны
x <= y && y < z
x /= y && y < z && z == a
Просто посмотреть, смогу ли я заставить синтаксис работать.
Так что я проделал большую часть пути, используя пару классов типов:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
module ChainedOrd where
import Prelude hiding ((<), (<=), (>), (>=), (==), (/=))
class Booly v a where
truthy :: v -> a
falsy :: v -> a
instance Booly a Bool where
truthy = const True
falsy = const False
instance Booly a (Maybe a) where
truthy = Just
falsy = const Nothing
class ChainedOrd a b where
(<),(>),(<=),(>=),(==),(/=) :: (Booly b c) => a -> b -> c
infixl 4 <
infixl 4 >
infixl 4 <=
infixl 4 >=
infixl 4 ==
infixl 4 /=
instance Ord a => ChainedOrd a a where
x < y = case compare x y of LT -> truthy y ; _ -> falsy y
x > y = case compare x y of GT -> truthy y ; _ -> falsy y
x <= y = case compare x y of GT -> falsy y ; _ -> truthy y
x >= y = case compare x y of LT -> falsy y ; _ -> truthy y
x == y = case compare x y of EQ -> truthy y ; _ -> falsy y
x /= y = case compare x y of EQ -> falsy y ; _ -> truthy y
instance Ord a => ChainedOrd (Maybe a) a where
Just x < y = case compare x y of LT -> truthy y ; _ -> falsy y
Nothing < y = falsy y
Just x > y = case compare x y of GT -> truthy y ; _ -> falsy y
Nothing > y = falsy y
Just x <= y = case compare x y of GT -> falsy y ; _ -> truthy y
Nothing <= y = falsy y
Just x >= y = case compare x y of LT -> falsy y ; _ -> truthy y
Nothing >= y = falsy y
Just x == y = case compare x y of EQ -> truthy y ; _ -> falsy y
Nothing == y = falsy y
Just x /= y = case compare x y of EQ -> falsy y ; _ -> truthy y
Nothing /= y = falsy y
Который прекрасно компилируется, но, похоже, не позволяет создавать цепочки из-за проблемы промежуточных типов.
-- works
checkRange1 :: Ord a => a -> a -> a -> Bool
checkRange1 x y z = x `lem` y <= z
where lem :: Ord a => a -> a -> Maybe a
lem = (<=)
-- works
checkRange2 :: Ord a => a -> a -> a -> Bool
checkRange2 x y z = (x <= y) `leb` z
where leb :: Ord a => Maybe a -> a -> Bool
leb = (<=)
checkRange1
и checkRange2
работают нормально, так какоба они накладывают ограничение на промежуточный тип (либо в результате первого сравнения, либо в качестве аргумента для второго).
-- error
checkRange3 :: Ord a => a -> a -> a -> Bool
checkRange3 x y z = (x <= y) <= z
Когда я пытаюсь позволить компилятору выводить промежуточный тип, хотя, он лает на меня.
ChainedOrd.hs:64:30:
Ambiguous type variable `a0' in the constraints:
(ChainedOrd a0 a) arising from a use of `<='
at ChainedOrd.hs:64:30-31
(Booly a a0) arising from a use of `<=' at ChainedOrd.hs:64:24-25
Probable fix: add a type signature that fixes these type variable(s)
In the expression: (x <= y) <= z
In an equation for `checkRange3': checkRange3 x y z = (x <= y) <= z
Есть ли способ убедить компилятор, что он должен использовать Maybe a
в качестве промежуточного типа a0
, удовлетворяющий Booly a a0, ChainedOrd a0 a
, поскольку это единственный экземпляр, который он знаето?
Failingчто, есть ли другой способ сделать работу цепочки произвольного сравнения?