F# — это ML с ООП. Что ближе всего к обобщенным алгебраическим типам данных и классам типов Haskell?
Что ближе всего к Haskell GADT и классам типов в F#?
Ответы (2)
Ответ зависит от того, какую проблему вы пытаетесь решить. В F# нет классов типов и GADT, поэтому прямого сопоставления нет. Однако в F# есть различные механизмы, которые можно использовать для решения проблем, которые обычно решаются в Haskell с помощью GADT и классов типов:
Если вы хотите представлять структуры объектов и иметь возможность добавлять новые конкретные реализации с другим поведением, то часто можно использовать стандартные объектно-ориентированные объекты и интерфейсы.
Если вы хотите написать общий числовой код, вы можете использовать статические ограничения членов (вот пример), который, вероятно, технически наиболее близок к классам типов.
Если вы хотите написать более сложный общий код (например, универсальный принтер или синтаксический анализатор), вы часто можете использовать мощные возможности отражения во время выполнения F#.
Если вам нужно параметризовать код с помощью набора функций (которые выполняют различные подоперации, требуемые кодом), вы можете обойти реализацию интерфейса, как показывает @pad.
Существует также способ эмулировать классы типов Haskell в F#, но обычно это не идиоматическое решение F#, потому что стиль программирования F# во многом отличается от стиля Haskell. Однако одним из довольно стандартных способов использования этого является определение перегруженных операторов (см. этот ответ SO).
На метауровне вопрос о том, что является эквивалентом функции X на другом языке, часто приводит к запутанному обсуждению, поскольку X может использоваться для решения задач A, B, C на одном языке, в то время как другой язык может предоставлять другие функции для решения одних и тех же проблем (или некоторых проблем может вообще не быть).
В F# часто используются интерфейсы и наследование для этих целей.
Для примера приведем простой класс типов, использующий интерфейсы и выражения объектов:
/// Typeclass
type MathOps<'T> =
abstract member Add : 'T -> 'T -> 'T
abstract member Mul : 'T -> 'T -> 'T
/// An instance for int
let mathInt =
{ new MathOps<int> with
member __.Add x y = x + y
member __.Mul x y = x * y }
/// An instance for float
let mathFloat =
{ new MathOps<float> with
member __.Add x y = x + y
member __.Mul x y = x * y }
let XtimesYplusZ (ops: MathOps<'T>) x y z =
ops.Add (ops.Mul x y) z
printfn "%d" (XtimesYplusZ mathInt 3 4 1)
printfn "%f" (XtimesYplusZ mathFloat 3.0 4.0 1.0)
Это может выглядеть не очень красиво, но это F#-ишный способ сделать это. Для более похожего на Haskell решения, в котором используется словарь операций, вы можете взглянуть на этот хороший ответ< /а>.
let inline XtimesYplusZ x y z = (x * y) + z
, а XtimesYplusZ 3 4 1
и XtimesYplusZ 3.0 4.0 1.0
будут работать. Но, конечно, в другом сценарии код, основанный на стиле, который вы демонстрируете, был бы хорошим способом моделирования такого рода параметризации с помощью операций. Я думаю, это то, что я имею в виду, говоря, что существуют разные подходы к разным проблемам, решаемым классами типов в Haskell.
- person Tomas Petricek; 10.11.2012