Типы высшего порядка как переменные типа в Haskell

У меня есть класс:

import Linear

class Coordinate c where
    rotate :: Num a => Quaternion a -> c a -> c a
    translate :: Num a => V3 a -> c a -> c a

, для которого я определил экземпляры

instance Coordinate V3 where
    rotate _ = id
    translate p = (p+)

instance Coordinate Quaternion where
    rotate o = (o*)
    translate _ = id

Теперь я хочу определить экземпляр для пары членов класса.

instance (Coordinate a, Coordinate b) => Coordinate (a, b) where
    rotate o (a, b) = (rotate o a, rotate o b)
    translate p (a, b) = (translate p a, translate p b)

Проблема в том, что это не работает, так как компилятор ожидает аргумент для a и b. Однако добавление ограничения типа, например

instance (Coordinate a, Coordinate b, Num c) => Coordinate (a c, b c) where
    move p (a, b) = (move p a, move p b)
    translate p (a, b) = (translate p a, translate p b)

Это тоже не работает, так как в результате получается выражение вида *, а не * -> *. Я вижу, что оба вышеперечисленных варианта неверны, но я не уверен, как это решить. Я предполагаю, что должно быть какое-то ограничение, которое сохраняет типы Num для обоих a и b одинаковыми, но я не знаю, как это будет выглядеть синтаксически.


person notBob    schedule 23.12.2019    source источник


Ответы (1)


Вы не можете создать экземпляр этого класса Coordinate для встроенного типа пары. Вам нужно изменить один из них.

  • Класс Coordinate можно изменить, чтобы он принимал обычный Type в качестве аргумента:

    {-# LANGUAGE FlexibleContexts, TypeFamilies #-}
    
    import Data.Kind (Type)
    
    class Num (Component c) => Coordinate c where
        type Component c :: Type -- every Coordinate type has a Component type
        rotate :: Quaternion (Component c) -> c -> c
        translate :: V3 (Component c) -> c -> c
    

    Например, экземпляр V3 теперь будет выглядеть так

    instance Num a => Coordinate (V3 a) where
        type Component (V3 a) = a
        rotate _ = id
        translate = (+)
    

    И экземпляр пары будет использовать ограничение равенства, что вы и искали.

    instance (Coordinate a, Coordinate b, Component a ~ Component b) => Coordinate (a, b) where
        type Component (a, b) = Component a -- or = Component b
        rotate p (l, r) = (rotate p l, rotate p r)
        translate p (l, r) = (translate p l, translate p r)
    
  • Вместо пар используйте Product< /а>:

    import Data.Functor.Product
    
    instance (Coordinate a, Coordinate b) => Coordinate (Product a b) where
        rotate p (Pair l r) = Pair (rotate p l) (rotate p r)
        translate p (Pair l r) = Pair (translate p l) (translate p r)
    
person HTNW    schedule 23.12.2019