我有以下代碼:
[<AbstractClass>]
type Effect<'a>() =
class end
type Input<'a>(chan : Channel<'a>, cont : 'a -> Effect<'a>) =
inherit Effect<'a>()
member this.Chan = chan
member this.Cont = cont
type Output<'a>(value : 'a, chan : Channel<'a>, cont : unit -> Effect<'a>) =
inherit Effect<'a>()
member this.Value = value
member this.Chan = chan
member this.Cont = cont
type Parallel<'a, 'b>(eff1 : Effect<'a>, eff2 : Effect<'b>) =
inherit Effect<'a * 'b>()
member this.Eff1 = eff1
member this.Eff2 = eff2
type Return<'a>(value : 'a) =
inherit Effect<'a>()
member this.Value = value
let Send(value, chan, cont) = Output(value, chan, cont)
let Receive(chan, cont) = Input(chan, cont)
let rec NaiveEval (eff : Effect<'a>) =
match eff with
| :? Input<'a> as input -> ()
| :? Output<'a> as output -> ()
| :? Parallel<'a, 'b> as par -> () // ERROR: FS0193: Type constraint mismatch. The type 'Parallel<'a,'b> is not compatible with type 'Effect<'a>'.
| :? Return<'a> as ret -> ()
| _ -> failwith "Unsupported effect!"
我希望能夠以某種方式在 NaiveEval 函式中看到的 Effect 的不同子類上進行模式匹配。Parallel<'a, 'b>上的模式匹配給出以下錯誤:
FS0193:型別約束不匹配。'Parallel<'a,'b> 型別與'Effect<'a>' 型別不兼容。
我會假設在這種情況下'a將被推斷為'a * 'b但我想這不是正在發生的事情。
任何幫助表示贊賞。謝謝。
編輯馬克的回答:
據我了解,我想做的事情是不可能的。我從一個有區別的聯合開始,但我認為型別層次結構可以解決我的問題。我猜不會。
我以前使用過這個聯合:
type Effect<'a> =
| Input of Channel<'a> * ('a -> Effect<'a>)
| Output of 'a * Channel<'a> * (unit -> Effect<'a>)
| Parallel of Effect<'a> * Effect<'a>
| Return of 'a
我曾經使用這種型別,但我想并行為: Parallel of Effect<'a> * Effect<'b> 而不僅僅是兩個 'a。我認為這也不可能嗎?
uj5u.com熱心網友回復:
這是因為型別引數 inParallel被定義為對應于'a * 'b。換句話說, 的單一型別引數Effect<'a>是 here 'a * 'b。
'b如果將型別變數重命名為and ,可能更容易理解它們之間的對應關系'c:
type Parallel<'b, 'c>(eff1 : Effect<'b>, eff2 : Effect<'c>) =
inherit Effect<'b * 'c>()
member this.Eff1 = eff1
member this.Eff2 = eff2
現在你有了這個Parallel案例,'a = 'b * 'c.
編譯器錯誤正是因為您試圖重新定義什么'a。本質上,您是在試圖堅持'a = 'a * 'b. 這不起作用,因為您'a在等號的兩側都有。
(我們可以想象一個更復雜的型別系統允許這樣做。這基本上就像說x = x y,這只有在 時才有可能。在代數資料型別的世界中,這y = 0意味著那'b將是()(unit型別系統無法推斷,無論如何它可能不是你想要的。)
您可以通過顯式使用重命名的型別引數將錯誤換成編譯器警告:
let rec NaiveEval (eff : Effect<'a>) =
match eff with
| :? Input<'a> as input -> ()
| :? Output<'a> as output -> ()
| :? Parallel<'b, 'c> as par -> () // Warning FS0064
| :? Return<'a> as ret -> ()
| _ -> failwith "Unsupported effect!"
然而,警告是:
這種構造導致代碼不像型別注釋所指示的那樣通用。型別變數 'a 已被限制為型別 ''b * 'c'。
換句話說,該NaiveEval功能只能評估Effect<'b * 'c>- 可能仍然不是您想要的......?
這可能仍然有點難以接受,但想象一下你想在這個案例中做點什么。例如,這不會編譯,因為再一次,型別不對齊:parParallel
| :? Parallel<'b, 'c> as par ->
NaiveEval (par.Eff2)
在這里,par.Eff2有 type 'c,但您目前在 type 的函式“內部” Effect<'b * 'c> -> unit。你不能遞回到它,編譯器會告訴你:
錯誤 FS0001 ''c' 和 ''b * 'c' 型別不能統一。
順便說一句,您是否考慮過定義有區別的聯合而不是型別層次結構?
uj5u.com熱心網友回復:
我有很多個月前研究過無標簽編碼,但我似乎無法讓它在這種情況下作業(實際上我做得更進一步)。
我使用良好的老式和低調的訪問者模式(這是密切相關的,請原諒我的特殊 F# 已經有一段時間了,我認為我的方法簽名應該是元組的,但我直到最終版本才這樣做)。
foo 輸出 6,并遞回評估并行效果。
type Channel<'a>() = class end
type IEffectVisitor<'ret> =
abstract member VisitInput<'a> : Input<'a> -> 'ret
abstract member VisitOutput<'a> : Output<'a> -> 'ret
abstract member VisitParallel<'a,'b> : Parallel<'a,'b> -> 'ret
abstract member VisitReturn<'a> : Return<'a> -> 'ret
and IEffect<'a> =
abstract member Accept<'ret> : IEffectVisitor<'ret> -> 'ret
and Input<'a>(chan : Channel<'a>, cont : 'a -> IEffect<'a>) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitInput(this)
member this.Chan = chan
member this.Cont = cont
and Output<'a>(value : 'a, chan : Channel<'a>, cont : unit -> IEffect<'a>) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitOutput(this)
member this.Value = value
member this.Chan = chan
member this.Cont = cont
and Parallel<'a, 'b>(eff1 : IEffect<'a>, eff2 : IEffect<'b>) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitParallel(this)
member this.Eff1 = eff1
member this.Eff2 = eff2
and Return<'a>(value : 'a) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitReturn(this)
member this.Value = value
let rec NaiveEval (eff : IEffect<'a>) : int =
eff.Accept(
{ new IEffectVisitor<int> with
member this.VisitOutput x =
1
member this.VisitParallel x =
x.Eff1.Accept(this) x.Eff2.Accept(this)
member this.VisitReturn x =
3
member __.VisitInput x =
4 })
let foo() =
let x = Parallel(Return 1, Return "a")
printfn "%A" <| NaiveEval(x)
()
實際上考慮到這一點,我又回到了它,您可以一起丟失效果上的型別引數,而不會(在示例中)任何型別安全性的損失,這為更無標記的實作鋪平了道路。
type Channel<'a>() = class end
type IEffectVisitor<'ret> =
abstract member VisitInput<'a> : Input<'a> -> 'ret
abstract member VisitOutput<'a> : Output<'a> -> 'ret
abstract member VisitParallel<'a,'b> : Parallel -> 'ret
abstract member VisitReturn<'a> : Return<'a> -> 'ret
and IEffect =
abstract member Accept<'ret> : IEffectVisitor<'ret> -> 'ret
and Input<'a>(chan : Channel<'a>, cont : 'a -> IEffect) =
interface IEffect with
member this.Accept(v) = v.VisitInput(this)
member this.Chan = chan
member this.Cont = cont
and Output<'a>(value : 'a, chan : Channel<'a>, cont : unit -> IEffect) =
interface IEffect with
member this.Accept(v) = v.VisitOutput(this)
member this.Value = value
member this.Chan = chan
member this.Cont = cont
and Parallel(eff1 : IEffect, eff2 : IEffect) =
interface IEffect with
member this.Accept(v) = v.VisitParallel(this)
member this.Eff1 = eff1
member this.Eff2 = eff2
and Return<'a>(value : 'a) =
interface IEffect with
member this.Accept(v) = v.VisitReturn(this)
member this.Value = value
module Foo =
let Send(value, chan, cont) = Output(value, chan, cont)
let Receive(chan, cont) = Input(chan, cont)
let rec NaiveEval (eff : IEffect) : int =
eff.Accept(
{ new IEffectVisitor<int> with
member this.VisitOutput x =
1
member this.VisitParallel x =
x.Eff1.Accept(this) x.Eff2.Accept(this)
member this.VisitReturn x =
3
member __.VisitInput x =
4 })
let foo() =
let x = Parallel(Return 1, Return "a")
printfn "%A" <| NaiveEval(x)
()
which leads you onto something a bit tagless....(I always love this bit, becuase of the realisation that a factory pattern is actually a special case of a visitor - in a way, and all your classes and DUs evaporate into functions/methods).
type Channel<'a>() = class end
type IEffectAlgebra<'retInput,'retOutput,'retParallel,'retReturn> =
abstract member Input<'a> : Channel<'a> * ('a -> IEffect) -> 'retInput
abstract member Output<'a> : 'a * Channel<'a> * (unit -> IEffect) -> 'retOutput
abstract member Parallel<'a,'b> : IEffect * IEffect -> 'retParallel
abstract member Return<'a> : 'a -> 'retReturn
and IEffect =
abstract member Accept<'ret> : IEffectAlgebra<'ret,'ret,'ret,'ret> -> 'ret
type IEffectVisitor<'ret> = IEffectAlgebra<'ret,'ret,'ret,'ret>
type IEffectFactory = IEffectVisitor<IEffect>
let effectFactory =
{ new IEffectFactory with
member __.Input(arg1, arg2) =
{ new IEffect with
member __.Accept(v) = v.Input(arg1, arg2) }
member __.Output(arg1, arg2, arg3) =
{ new IEffect with
member __.Accept(v) = v.Output(arg1, arg2, arg3) }
member __.Parallel(arg1, arg2) =
{ new IEffect with
member __.Accept(v) = v.Parallel(arg1, arg2) }
member __.Return(arg1) =
{ new IEffect with
member __.Accept(v) = v.Return arg1 }
}
module Foo =
let Send(value, chan, cont) = effectFactory.Output(value, chan, cont)
let Receive(chan, cont) = effectFactory.Input(chan, cont)
let rec NaiveEval (eff : IEffect) : int =
eff.Accept(
{ new IEffectVisitor<int> with
member __.Input(_, _) = 1
member __.Output(_, _, _) = 2
member this.Parallel(arg1, arg2) =
arg1.Accept this arg2.Accept this
member __.Return(_) = 4 })
let foo() =
let x = effectFactory.Parallel(effectFactory.Return 1, effectFactory.Return "a")
printfn "%A" <| NaiveEval(x)
()
因此,對于 Return<> 要求,讓訪問者保持簡單,并介紹我認為所謂的幻像型別。見naiveeval2,忽略例外代碼......我不知道你的代碼實際上是做什么的,型別結正在處理Parallel型別和回傳型別......它不是型別安全的,你必須將Effect結構折疊成一個obj,然后轉換結果。如果您的 Effect 型別是型別安全的,我認為這段代碼實際上不可能失敗(就型別而言),我只是看不到 F# 編譯器驗證型別的方法,感覺它可能需要 GADT(我們不能這樣做)......但我懷疑Haskeller專家會告訴我們不同......我是一個非常不熟練的haskeller。
type Channel<'a>() = class end
type IEffectVisitor<'ret> =
abstract member VisitInput<'a> : Input<'a> -> 'ret
abstract member VisitOutput<'a> : Output<'a> -> 'ret
abstract member VisitParallel<'a,'b> : Parallel<'a,'b> -> 'ret
abstract member VisitReturn<'a> : Return<'a> -> 'ret
and IEffect<'a> =
abstract member Accept<'ret> : IEffectVisitor<'ret> -> 'ret
and Input<'a>(chan : Channel<'a>, cont : 'a -> IEffect<'a>) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitInput(this)
member this.Chan = chan
member this.Cont = cont
and Output<'a>(value : 'a, chan : Channel<'a>, cont : unit -> IEffect<'a>) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitOutput(this)
member this.Value = value
member this.Chan = chan
member this.Cont = cont
and Parallel<'a,'b>(eff1 : IEffect<'a>, eff2 : IEffect<'b>) =
interface IEffect<'a * 'b> with
member this.Accept(v) = v.VisitParallel(this)
member this.Eff1 = eff1
member this.Eff2 = eff2
and Return<'a>(value : 'a) =
interface IEffect<'a> with
member this.Accept(v) = v.VisitReturn(this)
member this.Value = value
module Foo =
let Send(value, chan, cont) = Output(value, chan, cont)
let Receive(chan, cont) = Input(chan, cont)
let rec NaiveEval (eff : IEffect<_>) : int =
eff.Accept(
{ new IEffectVisitor<int> with
member this.VisitOutput x =
1
member this.VisitParallel x =
x.Eff1.Accept(this) x.Eff2.Accept(this)
member this.VisitReturn x =
3
member __.VisitInput x =
4 })
let rec NaiveEval2 (eff : IEffect<'a>) : 'a =
eff.Accept(
{ new IEffectVisitor<obj> with
member this.VisitOutput x =
failwith ""
member this.VisitParallel x =
(x.Eff1.Accept(this), x.Eff2.Accept(this))
member this.VisitReturn x =
x.Value
member __.VisitInput x =
failwith ""
}) :?> 'a
let foo() =
let x = Parallel(Return 1, Return "a")
printfn "%A" <| NaiveEval(x)
()
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/415124.html
標籤:
