Что такое эквивалент 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]" и т.д.