Явное указание на функцию класса типа
Начиная с ghc-8.0 у нас есть очень хорошее расширение под названием TypeApplications
, Что позволяет нам вместо:
λ> show (5 :: Int)
"5"
сделай так что то подобное:
λ> :set -XTypeApplications
λ> show @Int 5
"5"
Что действительно круто. Это становится немного сложнее, когда мы добавляем больше переменных типа, но есть правила, которые можно использовать для точного определения порядка, и они очень хорошо документированы:
showFooBar :: (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b
Таким образом, в функции выше мы сначала поставим a
а потом b
:
λ> showFooBar @Int @Double 3 4
"3 and 4.0"
Это здорово, но что если я хотел бы изменить порядок? Нет проблем, мы можем использовать ExplicitForAll
расширение (или другое, что подразумевает это), чтобы указать это:
{-# LANGUAGE ExplicitForAll #-}
showFooBar :: forall b a . (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b
И теперь мы изменили порядок типов, которые мы собираемся применить:
λ> showFooBar @Int @Double 3 4
"3.0 and 4"
Проблема в том, что я не могу понять, как добиться того же эффекта для функций, которые являются частью класса типов. Рассмотрим этот пример:
{-# LANGUAGE MultiParamTypeClasses #-}
class (Show a, Show b) => FooBar a b where
fooBarClassFunc :: a -> b -> String
Я не могу поставить forall
на функцию сейчас (например. fooBarClassFunc :: forall a b . a -> b -> ..
, потому что меняет смысл функции и, очевидно, не компилируется.
Итак, вопрос в том, как изменить порядок переменных типа с целью TypeApplication
внутри методов класса класса?
редактировать
На всякий случай я попробовал InstanceSigs
расширение, и оно полностью игнорирует порядок forall
переменные типа, насколько TypeApplications
обеспокоены, что хорошо, в противном случае мы бы в конечном итоге поведение, которое определяется экземпляром, а не классом.
1 ответ
как изменить порядок переменных типа с целью
TypeApplication
внутри методов класса класса?
Я думаю, что ответ @ luqui достаточно хорош. Но почему бы не это
class (Show b, Show a) => FooBar b a where
fooBarClassFunc :: a -> b -> String
У вас есть только один метод, поэтому единственное соображение, определяющее порядок параметров в классе, заключается в TypeApplication
внутри методы.
Если у вас есть два или более методов, для которых вы хотите порядок TypeApplication
быть другим (точка @ chi, но почему?), то для других методов либо предложение luqui, либо (эквивалентно) другой класс с ограничением суперкласса и реализацией по умолчанию.
class (Show a, Show b, FooBar b a) => OtherFooBar a b where
otherFooBarClassFunc :: a -> b -> String
otherFooBarClassFunc = otherFooBarClassFunc' -- default
instance {-# NOOVERLAPPABLE #-} OtherFooBar a b where {} -- take default
(Предполагается, что otherFooBarClassFunc'
определяется в основном классе; и вот где продолжается определение реального экземпляра.)
Конечно, многое можно сказать о одном методе в классе.
{-# NOOVERLAPPABLE #-}
что мы не делаем, это моя маленькая шутка.