«Проблема выражения» разрешима в F #? - PullRequest
8 голосов
/ 23 октября 2011

Я смотрел интересное видео , в котором классы типов в Haskell используются для решения так называемой «проблемы выражения». Примерно через 15 минут показано, как можно использовать классы типов для «открытия» типа данных, основанного на дискриминированном объединении для расширения - дополнительные дискриминаторы могут быть добавлены отдельно без изменения / перестройки исходного определения.

Я знаю, что классы типов не доступны в F #, но есть ли способ использовать другие возможности языка для достижения такого рода расширяемости? Если нет, то насколько мы можем приблизиться к решению проблемы выражения в F #?

Разъяснение: я предполагаю, что проблема определена, как описано в предыдущем видео в серии - расширяемость типа данных и операций над этим типом с функциями модульности на уровне кода и отдельной компиляции (расширения могут быть развернуты как отдельные модули без необходимости изменять или перекомпилировать исходный код), а также безопасность статического типа.

Ответы [ 3 ]

3 голосов
/ 23 октября 2011

Как отметил в комментарии Йорг, все зависит от того, что вы подразумеваете под решить .Если вы имеете в виду решить , включая некоторую форму проверки типов, что вы не пропускаете реализацию какой-то функции для некоторого случая, то F # не дает вам никакого элегантного способа (и я не уверен, что решение Haskell элегантно).Вы можете закодировать его с помощью решения SML, упомянутого kvb, или с помощью одного из решений OO .

В действительности, если бы я разрабатывал реальную систему, которая должна решить проблему, я бы выбрал решение, которое не дает вам полной проверки, но намного проще в использовании.

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

[<Extends("Expr")>]  // Specifies that this type should be treated as a case of 'Expr'
type App = App of obj * obj

module AppModule = 
  [<Implements("format")>] // Specifies that this extends function 'format'
  let format (App(e1, e2)) =
    // We don't make recursive calls directly, but instead use `invoke` function
    // and some representation of the function named `formatFunc`. Alternatively
    // you could support 'e1?format' using dynamic invoke.
    sprintfn "(%s %s)" (invoke formatFunc e1) (invoke formatFunc e2)

Это не дает вам никакой проверки типов, но дает довольно элегантное решение, которое легко использовать и не так сложно реализовать.(используя отражение).Проверка того, что вы не пропустили регистр, не выполняется во время компиляции, но вы можете легко написать модульные тесты для этого.

3 голосов
/ 23 октября 2011

См. Комментарий Весы Карвонен здесь для одного решения SML (хотя и громоздкого), которое можно легко перевести на F #.

2 голосов
/ 28 декабря 2012

Я знаю, что классы типов не доступны в F #, но есть ли способ использовать другие возможности языка для достижения такого рода расширяемости?

Не верю, нет.

Если нет, то насколько мы можем приблизиться к решению проблемы выражения в F #?

Проблема с выражением заключается в том, что пользователь может дополнить код вашей библиотеки новыми функциями и новыми типами без необходимости перекомпиляции библиотеки. В F # типы объединения упрощают добавление новых функций (но невозможно добавлять новые случаи объединения к существующему типу объединения), а типы классов облегчают получение новых типов классов (но невозможно добавлять новые методы в существующую иерархию классов). , Это две формы расширяемости, требуемые на практике. Возможность расширения в обоих направлениях одновременно без ущерба для безопасности статического типа - это просто академическое любопытство, IME.

Между прочим, самый элегантный способ обеспечить такой вид расширяемости, который я видел, - это пожертвовать безопасностью типов и использовать так называемое «программирование на основе правил». Mathematica делает это. Например, функция для вычисления символической производной выражения, являющегося целочисленным литералом, переменной или сложением, может быть написана в Mathematica следующим образом:

D[_Integer, _] := 0
D[x_Symbol, x_] := 1
D[_Symbol, _] := 0
D[f_ + g_, x_] := D[f, x] + D[g, x]

Мы можем модифицировать поддержку умножения следующим образом:

D[f_ g_, x_] := f D[g, x] + g D[f, x]

и мы можем добавить новую функцию для оценки выражения следующим образом:

E[n_Integer] := n
E[f_ + g_] = E[f] + E[g]

Для меня это гораздо более элегантно, чем любое из решений, написанных на таких языках, как OCaml, Haskell и Scala, но, конечно, это небезопасно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...