#haskell #types
#haskell #типы
Вопрос:
С помощью функциональных зависимостей я могу ограничить тип зависимого параметра в классе типов, используя многопараметрические классы типов. Точно так же:
{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses,TypeSynonymInstances #-}
class (Num a, Integral b) => F a b | a -> b where
f :: a -> b
instance F Int Int where
f = id
instance F Float Integer where
f = truncate
И все будет работать идеально.
> f (1 :: Int)
1
> f (1.9 :: Float)
1
Но если я попытаюсь написать что-то вроде
instance F Double String where
f = show
Я получу следующую ошибку компиляции:
No instance for (Integral String)
arising from the superclasses of an instance declaration
Possible fix: add an instance declaration for (Integral String)
In the instance declaration for `F Double String'
Есть ли способ подойти к этому с семействами типов вместо fundeps?
Ответ №1:
В принципе, нет, и на самом деле это не имеет ничего общего с функциональными зависимостями (или семействами типов). Ваше определение класса имеет
class (Num a, Integral b) => F a b
который объявляет, что должен существовать встроенный экземпляр для b
. У String
A нет экземпляра Integral, поэтому у вас не может быть ничего подобного форме, F a String
если вы не определите
-- String is a type synonym for [Char]
instance Integral [Char] where
Я не знаю, было бы ли это разумно в целом. Если бы имело смысл создать интегральный экземпляр для строк в вашей системе, вы, вероятно, захотели бы поместить оболочку newtype вокруг String и создать экземпляр для этого вместо этого.
Комментарии:
1. Это был просто пример. Я не собираюсь делать строки целыми или наоборот, я хочу ограничить тип b (параметр, который зависит от a ) целым в определении класса!
2. Но вы не можете хотеть ограничивать это и в то же время хотеть экземпляр F, где это ограничение не выполняется! Что должен делать плохой компилятор? Сделать исключение, потому что это вы?
3. Я не хочу иметь экземпляр F, где ограничение не выполняется, это просто пример ошибки, которую должен выдать компилятор. В этом нет ничего плохого! И мне интересно, мог бы я переписать этот код (который завершается ошибкой в
F Double String
экземпляре, но работает сinstance F Int Int
), используя семейства типов.4. Я интерпретировал ваш вопрос как «Это не компилируется, могу ли я заставить его компилироваться с помощью AT вместо FunDeps?». Часть со строками / ошибкой компилятора совершенно не имеет значения, вероятно, было бы более понятно, если бы вы убрали ее. Поскольку вы включили ограничение суперкласса в версию fundep, ясно, что любое решение AT также должно было бы учитывать их.
Ответ №2:
Я думаю, вы хотите иметь что-то вроде этого:
{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
class Num a => G a where
type B a
g :: (Integral (B a)) => a -> B a
instance G Int where
type B Int = Int
g = id
instance G Float where
type B Float = Integer
g = truncate
В этом примере напрямую используется не семейство типов, а связанный синоним типа, который активируется тем же флагом. Идея довольно проста. Вместо явного указания второго параметра для класса типов мы определяем синоним типа, который заполняется соответствующим типом.
Я также впервые использую синонимы связанных типов, но, похоже, это довольно интересная функция.
Комментарии:
1. Спасибо за объяснение. Это именно то, что я имел в виду.