Явное указание на функцию класса типа - PullRequest
0 голосов
/ 15 февраля 2019

Начиная с 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 Ответ

0 голосов
/ 15 февраля 2019

как изменить порядок переменных типа с целью TypeApplication внутри методов класса типов?

@ luqui ответ достаточно хороший, я думаю.Но почему бы и нет:

class (Show b, Show a) => FooBar b a where
  fooBarClassFunc :: a -> b -> String

У вас есть только один метод, поэтому единственное соображение, связывающее порядок параметров с классом, касается TypeApplication внутри методов.

Если у вас есть два или более методов, для которых вы хотите, чтобы порядок TypeApplication был разным (точка @ chi, но почему?), То для других методов либо предложение Луки, либо (эквивалентно) другой класс с ограничением суперклассаи реализация по умолчанию.

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

...