Как кодировать в словаре несколько функций разных типов
Я строю переводчик в F#. Я пытаюсь быть умным и обобщать интерпретацию примитивных операторов как вызовов функций (в данном случае как сокращений).
Это идея:
let reduce fx values =
Array.reduce values fx
let primitivesEnv =
let r = Dictionary<string,'T -> 'T -> 'T>()
r.["int_+"] <- reduce (fun(l:int, r:int) -> l + r)
r.["int_-"] <- reduce (fun(l:int, r:int) -> l - r)
r
Так что я мог позже сделать это:
env.["int_+"]([| 1, 2 |])
Конечно, тип проверки отвергает это с
Предупреждение FS0064: эта конструкция приводит к тому, что код будет менее общим, чем указано в аннотациях типов. Переменная типа 'T была ограничена типом''a -> 'a -> 'a'. Ошибка FS0001: несоответствие типов. Ожидание ('a -> 'a -> 'a) -> ('a -> 'a -> 'a) -> 'a -> 'a -> 'a, но с учетом (' a -> 'a -> 'a) ->' a Полученный тип будет бесконечным при объединении '' a 'и' ('a ->' a -> 'a) ->' a -> 'a ->' a '
П.Д.: Я знаю, как сделать это как простой интерпретатор, но я пытаюсь найти решение, которое позволило бы мне создавать десятки методов в общем виде, без создания МАТЧ для каждого.
1 ответ
Прежде всего, есть некоторые проблемы с вашим кодом. Вы добавили аннотацию типа в свой словарь 'T -> 'T -> 'T
но мне кажется, что он должен возвращать функцию типа 'T[] -> 'T
так как вы пытаетесь дать ему массив и оценить сокращение.
Также, [| 1, 2 |]
это массив из одного кортежа, вам нужен массив из нескольких значений, такой как этот: [| 1; 2 |]
Я исправил ваш код следующим образом:
let reduce values =
Array.reduce values
let primitivesEnv =
let r = System.Collections.Generic.Dictionary<string,'T[] ->'T>()
r.["int_+"] <- reduce ((+) : int -> int -> int)
r.["int_-"] <- reduce ((-) : int -> int -> int)
r
let result = primitivesEnv.["int_+"] [| 1; 2 |]
К сожалению, это не конец проблем.
Мы могли бы сказать, что словарь имеет тип 'T[] ->'T
но это не так, единственный действительный тип для словаря int[] -> int
, первый int -> int -> int
аннотация типа создает это ограничение. Если мы пропустим эту аннотацию типа, 'T
ограничен int
когда мы используем его с int[]
,
Параметр типа 'T
всегда должен быть определенным образом определен как фиксированный тип, это не шаблон, который позволяет вам использовать что-либо.
Это означает, что подход словаря хорошо, пока вы не хотите добавить float
(или какой-то другой тип) в дополнение к просто int
, Единственный вариант при использовании словаря - либо выбрать один тип, либо отбросить часть безопасности типов и разрешить конкретный тип во время выполнения.
Самое простое изменение, которое можно сделать, это создать несколько объединений для описания различных типов сокращения:
type Reduction =
|IntReduction of (int[] -> int)
|FloatReduction of (float[] -> float)
Затем вы создаете Dictionary<string, Reduction>
вместо Dictionary<string,'T[] ->'T>
,
Когда дело доходит до более общей проблемы создания интерпретатора в F#, я бы начал с создания структурированного набора различающихся объединений, чтобы описать выражения и структуру мини-языка, который вы хотите интерпретировать, это называется абстрактным синтаксическим деревом (АСТ).
Затем вы можете определить run
функция, которая проходит по дереву и выполняет все вычисления, описанные в AST.
Я бы также использовал библиотеку синтаксических анализаторов (я бы порекомендовал FParsec) для анализа любого структурированного текста в абстрактном синтаксическом дереве случаев объединения, которые вы определили в предыдущем шаге.
У Филиппа Трелфорда есть онлайн пример того, как сделать это с помощью FParsec для простого C# AST: http://www.fssnip.net/lf Это, вероятно, гораздо более мощный инструмент, чем вам нужно, но, надеюсь, он даст вам отправную точку,