Шаблон расширения Haskell, соответствующий типу
Кто-нибудь знает о расширении, которое позволило бы установить ограничения типа для сопоставления с образцом? Например:
{Language Extension}
IsOrderable::(Eq a)=>a->IO Bool
IsOrderable x = case x of
o::(Ord a=>a) -> do
putStrLn "This equatable thing is also orderable."
return True
_ -> do
putStrLn "This equatable thing is not orderable."
return False
Примечание: я спрашиваю об этом, чтобы я мог создавать монады, которые реагируют по-разному в зависимости от типа ввода. В частности, я пытался сделать монаду вероятности, но я хотел бы проверить, является ли тип ввода уравновешенным, чтобы я мог комбинировать дубликаты.
2 ответа
Есть способ, но это не красиво. Сначала мы сделаем функцию, которую мы хотим видеть dispatch для экземпляра класса type.
class IsOrd a where
isOrd :: a -> IO Bool
isOrd
в конечном итоге будет два экземпляра, соответствующие два каждый из ваших case
s. Конечно, простой
instance Ord a => IsOrd a where
isOrd = putStrLn "Orderable!" >> return True
instance IsOrd a where
isOrd = putStrLn "Not orderable" >> return False
Не будет работать, потому что экземпляр головы (IsOrd
) одинаковы, и компилятор работает так, что он соответствует заголовку экземпляра независимо от того, выполняется ограничение или нет, и только после этого проверяет ограничение.
Теперь начинается сложная часть: она описана здесь и здесь. (Вторая ссылка, сборник Олега Киселева по классам типов, может содержать другие важные материалы, которые я не включаю в этот список, потому что пока не знаю об этом. В целом, это также отличный ресурс!). Суть его заключается в том, чтобы:
data HTrue
data HFalse
instance (Ord a) => IsOrd' HTrue a where
isOrd' _ x = putStrLn "Orderable." >> return True
instance IsOrd' HFalse a where
isOrd' _ x = putStrLn "Not orderable" >> return False
Где мы добавили дополнительный логический уровень на уровне типа к голове экземпляра, чтобы в вашем примере были case
S становятся отличными экземплярами головы. Довольно изящная идея!
Есть и другие детали, о которых вам нужно беспокоиться, некоторые из которых я не до конца понимаю, но все равно достаточно легко заставить их работать: вот код, который делает то, что вы хотите (вам придется все время обрабатывать экземпляры Eq это из IsOrd
и далее, если вы хотите Eq
ограничение тоже). И вот конечный результат:
*Scratch> :l scratch.hs
[1 of 1] Compiling Scratch ( scratch.hs, interpreted )
Ok, modules loaded: Scratch.
*Scratch> isOrd (5::Int)
Orderable.
True
*Scratch> isOrd ('c')
Not orderable
False
Я не знаю такого расширения, и, кроме того, я подозреваю, что было бы очень трудно создать такое расширение с помощью текущего метода реализации классов GHC. В частности, GHC выполняет классы, передавая "доказательства" того, что данный тип реализует данный класс; поэтому, когда вы видите foo :: Eq a => a -> IO Bool
это действительно значит foo
принимает два аргумента: один является a
а другой является доказательством того, что существует Eq a
пример. Это доказательство не поддавалось.
Теперь подумайте о том, что происходит в предложенном вами коде: у вас в области есть значение типа a
и доказательство того, что есть Eq a
экземпляр, а затем вы задаете вопрос: есть ли в этой программе доказательства того, что есть Ord a
пример? Более того, я не собираюсь говорить вам заранее, что a
является. Пожалуйста, дайте мне правильный ответ на мой вопрос в любом случае, спасибо!
Вот самый канонический хитрый пример, который я могу придумать.
{-# LANGUAGE ExistentialQuantification #-}
data Existential = forall a. Eq a => Existential a
tricky :: Existential -> IO Bool
tricky (Existential x) = isOrderable x
Existential
type оборачивает значение другого типа, помня только две вещи: значение, которое мы обернули, и некоторые доказательства того, что есть Eq
введите для этого экземпляра. В частности, сам тип даже не запоминается! Так что теперь, когда приходит время бежать tricky
, у нас есть значение некоторого типа (но мы забыли, какой именно) и доказательство того, что рассматриваемый тип является экземпляром Eq
, Теперь, как мы собираемся угадать, является ли это также Ord
? Мы не можем знать - мы даже не знаем, какой экземпляр искать! (Действительно, мы даже не можем реально проверить значение разумным способом, чтобы попытаться восстановить, какой это тип; единственная проверка, которую мы можем сделать, - это операции, предлагаемые Eq
тип класс. Это не говорит нам много.) Мы ищем Ord Int
пример? Или может быть Ord (Complex Double)
пример? Без понятия.
С другой стороны, вы уже можете сделать что-то вроде этого:
{-# LANGUAGE DefaultSignatures #-}
class Eq a => QueryOrd a where
isOrd :: a -> IO Bool
default isOrd :: Ord a => a -> IO Bool
isOrd _ = putStrLn "yup" >> return True
isOrdAltDef :: Eq a => a -> IO Bool
isOrdAltDef _ = putStrLn "nope" >> return False
instance Eq a => QueryOrd (Complex a) where isOrd = isOrdAltDef
instance QueryOrd MyFancyType where isOrd = isOrdAltDef
... но вам понадобится один экземпляр QueryOrd
для каждогоOrd
экземпляр Eq
,