Явное указание на функцию класса типа

Начиная с 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 #-} что мы не делаем, это моя маленькая шутка.

Другие вопросы по тегам