Что такое эквивалент Haskell Java Curiously Recurring Generic Pattern?

A question только что придумал дженерики java. Код примера:

public interface A < T extends A < T> > {

}  

Связанный вопрос задает вопрос о

Class Enum<E extends Enum<E>> ...

Когда я пытаюсь прочитать java-документацию об этих родовых выражениях, мои глаза затуманиваются, и это греческий для меня.

Я надеюсь, что смогу понять их с точки зрения эквивалента Haskell!

Что эквивалентно (или аналогично) в Haskell одного или обоих этих примеров?

Ответ 1

Этот трюк используется, чтобы позволить интерфейсу ссылаться на конкретный тип реализации, например, чтобы обеспечить, чтобы тип аргумента и тип результата были того же типа, что и класс реализации, в чем-то вроде этого:

public interface Num<A extends Num<A>> {
    A add(A other); 
}

Это похоже на то, что вы получаете бесплатно с классами классов в Haskell:

class Num a where
    (+) :: a -> a -> a

Ответ 2

Это интересно, поскольку это тоже озадачило меня. Попробуем смоделировать его. Я, вероятно, не вернусь к тому же, что идиома используется для Java.

Если тип A наследуется от типа B, в функциональной земле это означает, что существует функция B -> A. Не беспокойтесь о разнице между классами и интерфейсами на данный момент; в функциональном переводе мало различий (интерфейс - это просто запись функций). Позвольте сделать нерекурсивный перевод, чтобы почувствовать его:

interface Showable {
    string show();
}

interface Describer<T extends Showable> { }

Перевод на записи функций:

data Showable = Showable { show :: String }

data Describer t = Describer { showable :: t -> Showable }

Если мы забудем о downcasting, то если у нас есть какой-то объект в Java, так что все, что мы знаем об этом, это то, что оно является Showable, тогда оно соответствует наличию объекта Showable в Haskell. На поверхности, проходящей через Showable и проходящей через a string, чувствую себя как разные вещи, но они эквивалентны.

Ограничение extends Showable входит в это, если мы имеем a Describer t, тогда мы знаем, что t "is" Showable; т.е. существует функция t -> Showable.

makeDescriber :: (t -> Showable) -> Describer t
makeDescriber f = Describer { showable = f }

Теперь откройте гаммар, используя полиморфизм.

interface Number<A extends Number<A>> {
    A add(A other); 
}

перевод на запись функций

data Number a = Number {
    add :: a -> a,
    number :: a -> Number a
}

Итак, если мы имеем a Number a, то мы знаем, что A "является" a Number a; т.е. существует функция a -> Number a.

Экземпляры java-интерфейса Number становятся функциями типа.

intNumber :: Integer -> Number Integer
intNumber x = Number { add = \y -> x + y, number = intNumber }

Эта функция соответствует class Integer extends Number<Integer>. Если у нас есть два целых числа x и y, мы можем добавить их, используя этот стиль "OO":

z :: Integer -> Integer -> Integer
z x y = intNumber x `add` y

А как насчет общей функции:

T Add< T extends Number<T> >(T x, T y) { return x.add(y); }

(hmm - правильный синтаксис Java? Мой опыт этого стиля от С#)

Помните, что ограничения становятся функциями, поэтому:

add' :: (t -> Number t) -> t -> t -> t
add' n x y = n x `add` y

Конечно, в Haskell мы видим, как объединение объекта вместе с поддерживаемыми им операциями является запутанным, поэтому мы предпочитаем их разделять:

data Num t = Num { add :: t -> t -> t }

add' :: Num t -> t -> t -> t
add' n x y = add n x y

И мы создаем словарь Num с фактическими операциями, например.

integerNum :: Num Integer
integerNum = Num { add = (+) }

Typeclasses - это всего лишь немного синтаксического сахара по этой последней идее.

Может быть, это помогает? Я просто хотел посмотреть, как это будет переводить буквально.

Ответ 3

Я не знаю, есть ли эквивалентный оператор для Haskell, но у Haskell есть typeclasses. Например, Show является классом типов, и многие объекты "расширяют" или "реализуют" шоу, то есть вы можете "показать 3", "показать [1,2,3,4]" и т.д.