Как кодировать в словаре несколько функций разных типов

Я строю переводчик в 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 Это, вероятно, гораздо более мощный инструмент, чем вам нужно, но, надеюсь, он даст вам отправную точку,

Другие вопросы по тегам