2014-09-17 1 views
3

F #에서 Clojure Transducers의 구현을 실험하고 있었는데, 두려운 값 제한 오류가 발생했습니다.값 제한 문제

트랜스 듀서 전체를 구성 가능해야합니다. 이것은 몇 가지 예제 코드입니다 :

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으로 수정하므로 seq과 동일한 xform을 사용할 수 없으며 적어도 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-stay-loose? lq = 1 –

답변

3

위에서 제안한 것처럼 오류 메시지 자체에 명시 적으로 인수를 추가 할 수 있습니까?

let xform x = x |> map ... 

F 번호는 무료 포인트를 너무 잘 따라한다 무엇 명시 적으로 xform 주석에 대해

+0

이것은 전화 사이트이며, 나에게 맞는 구문은 아닙니다. –

4

접근?

[<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

[<GeneralizableValue>]mapxform이 값 제한 적용 여부에 영향을 주석 것인가? (어쨌든 map은 람다에 의해 정의되었으므로 이미 일반화 가능하며 모든 inline의 요점도 표시되지 않습니다.)

요구 사항이있는 경우 :

  • xform
  • xform
(이 경우 (>>)) 연산자의 응용 프로그램에 의해 정의되는 일반적인 수 있지만 명시 적으로 주석 형 기능을한다

당신은 운이 없어. xform의 몸체는 일반화 할 수있는 표현식이 아니므로 (F # 스펙의 §14.7 참조) 값 제한이 여기에 적용됩니다.

또한 나는 이것이 합리적이라고 주장한다. 값 제한이 적용되지 않았다는 것을 상상하고, 우리가 map의 정의를 쥐게 것을 :

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!"을 기대하는 경우 :

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

이제 이러한 정의 하나씩 입력 인쇄 된거야? 실제 행동이 당신의 기대와 일치합니까? 내 의견으로는 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 트랜스 듀서의 기능 구성을 정말 좋아합니다. 누구나 이미 기능 구성을 알고 있고 새로운 운영자를 소개하는 것은 내가하고 싶은 것이 아닙니다. F # 컴파일러는 부작용이 없다는 것을 증명할 수 없기 때문에'map'에는 부작용이 없으며 여기에 값 제한이 있다는 가정이 있습니다. –

+0

예, 잠재적 인 부작용이있는 경우 유형 안전을 유지하려면 값 제한과 같은 것이 필요합니다. – kvb

+0

F # 컴파일러는 값 제한을 완화하기 위해 부작용이 없음을 증명하기 위해 더 많은 작업을 수행 할 수 있습니까? –