2014-09-17 5 views
3

Я экспериментировал с реализацией Clojure Transducers в F # и быстро попадал в ужасную ошибку ограничения значения.Проблемы с ограничением стоимости

Вся суть преобразователей должна быть составной. Это некоторые примеры кода:

type Reducer<'a,'b,'c> = ('a -> 'b -> 'a) -> 'a -> 'c -> 'a 

module Transducers = 
    [<GeneralizableValue>] 
    let inline map proj : Reducer<'result,'output,'input> = 
     fun xf -> 
     fun result input -> 
      xf result (proj input) 

    let inline conj xs x = x :: xs 
    let inline toList xf input = List.fold (xf conj) [] input 

    let xform = map (fun i -> i + 9) >> map (fun a -> a * 5) 
    //let xs = toList xform [1;2] // if you apply this, type will be fixed to 'a list 
           // which makes xform unusable with eg 'a seq 

Play on dotnetfiddle

GeneralizableValue должен был снять ограничение стоимости, но ничего не делает, это кажется. Ваша задача состоит в том, чтобы скомпилировать этот код без применения toList (Тип ввода будет фиксировать тип до 'a list, поэтому вы не можете использовать тот же xform с seq) и без изменения типа xform (по крайней мере, не так, чтобы сделайте его непригодным). Это просто невозможно в F #?

+3

Что случилось с просто добавлением аргумента в 'xform', как показывает ошибка? то есть «let xform x = x |> map ...' –

+0

Хм, кажется, вы достигли предела вывода типа F #. http://stackoverflow.com/questions/1131456/understanding-f-value-restriction-errors –

+0

И немного больше анализа здесь: http://stackoverflow.com/questions/1809405/forcing-f-type-inference-on -generics-and-interfaces-to-stay-loose? lq = 1 –

ответ

3

Как было предложено выше, и в самом сообщении об ошибке вы можете явно добавить аргументы?

let xform x = x |> map ... 

F # только играет так хорошо с точки свободного подходы

+0

Это сайт-вызов, и это не совсем синтаксис для меня. –

4

насчет аннотирования xform явно?

[<GeneralizableValue>] 
    let xform<'t> : Reducer<'t, _, _> = map (fun i -> i + 9) >> map (fun a -> a * 5) >> map (fun s -> s + 1) 
+0

У меня было это работает, но это просто слишком уродливо, чтобы заставить пользователей lib. Представьте, что вам нужно сделать то же самое при вызове Seq.map. –

4

Почему аннотирования бы map с [<GeneralizableValue>] влияют ли xform подлежит ограничению стоимости? (в любом случае map уже обобщается, так как он определяется лямбдой, также я не вижу точки всех inline s).

Если ваши требования:

  • xform должен быть универсальным, но не явно аннотированный функция типа
  • xform определяется применением оператора ((>>) в данном случае)

, тогда вам не повезло; Тело xform не является обобщаемым выражением (см. §14.7 в спецификации F #), поэтому здесь применяется ограничение значения.

Кроме того, я бы сказал, что это имеет смысл. Представьте себе, что ограничение значения не применяются, и что мы подправили определение map:

let map proj : Reducer<_,_,_> = 
    printfn "Map called!" 
    fun xf result input -> 
     xf result (proj input) 

Теперь введите эти определения один за другим:

let xform<'a> : Reducer<'a,int,int> = map (fun i -> i + 9) >> map (fun a -> a * 5) 

let x1 = xform (+) 
let x2 = xform (*) 
let x3 = xform (fun s i -> String.replicate i s) 

Когда вы ожидаете "Map called!" быть печататься? Соответствует ли реальное поведение вашим ожиданиям? По-моему, хорошо, что F # заставляет вас уйти с пути, чтобы рассматривать нецензурные ценности как общие значения.

Таким образом, вы не получите именно то, что хотите. Но, возможно, есть другая кодировка, которая будет работать так же хорошо для ваших случаев использования.Если каждые редуктора будет общим в типе результата, то вы могли бы сделать это вместо:

type Reducer<'b,'c> = abstract Reduce<'a> : ('a -> 'b -> 'a) -> 'a -> 'c -> 'a 

module Transducers = 
    let map proj = 
     { new Reducer<_,_> with 
      member this.Reduce xf result input = xf result (proj input) } 

    let (>!>) (r1:Reducer<'b,'c>) (r2:Reducer<'c,'d>) = 
     { new Reducer<_,_> with 
      member this.Reduce xf result input = (r1.Reduce >> r2.Reduce) xf result input } 

    let conj xs x = x :: xs 
    let toList (xf:Reducer<_,_>) input = List.fold (xf.Reduce conj) [] input 

    let xform = map (fun i -> i + 9) >!> map (fun a -> a * 5) 

К сожалению, вы должны поднять каждый оператор, как (>>) до уровня редуктора, прежде чем вы можете использовать его, но это, по крайней мере, работает для вашего примера, поскольку xform больше не является общим значением, а является не общим значением с помощью общего метода.

+0

Спасибо! У меня было что-то в этом роде, но мне очень нравится функциональная композиция датчиков Clojure. Все уже знают функциональный состав, а введение новых операторов - это не то, что я хотел бы сделать. Предполагается, что 'map' не будет иметь побочных эффектов, и здесь существуют ограничения по стоимости, потому что компилятор F # не может доказать, что у него нет побочных эффектов? –

+0

Да, при наличии потенциальных побочных эффектов для поддержания безопасности типа необходимо что-то вроде ограничения стоимости. – kvb

+0

Может ли компилятор F # сделать больше, чтобы доказать, что никаких побочных эффектов нет, чтобы смягчить ограничения стоимости? –

Смежные вопросы