type - ¿Cómo traduzco una restricción de parámetro de tipo genérico T: U de C#a F#?
tipos de parametros c# (2)
No creo que sea posible escribir restricciones como esta en F # (aunque no estoy muy seguro de por qué). De todos modos, sintácticamente, querrías escribir algo como esto (como sugiere Brian):
type FinallyBuilder<''T> (finallyAction : ''T -> unit) =
member this.Bind<''A, ''B when ''A :> ''T>(x : ''A) (cont : ''A -> ''B) = //''
try cont x
finally finallyAction (x :> ''T)
Desafortunadamente, esto da el siguiente error:
error FS0698: Restricción no válida: el tipo utilizado para la restricción está sellado, lo que significa que la restricción solo puede ser satisfecha por una solución como máximo
Este parece ser el mismo caso que el que se discute en esta lista de correo . Donde Don Syme dice lo siguiente:
Esta es una restricción impuesta para que la inferencia de tipo F # sea manejable. En particular, el tipo a la derecha de una restricción de subtipo debe ser nominal. Tenga en cuenta que las restricciones de la forma ''A:>'' B siempre se resuelven con entusiasmo en ''A ='' B, como se especifica en la sección 14.6 de la especificación F #.
Siempre puede resolver esto utilizando obj
en la función pasada a su constructor.
EDITAR : Incluso cuando usa obj
, los valores se unen usando let!
tendrá tipos más específicos (cuando se llame a finallyAction
, F # convertirá automáticamente el valor de algún parámetro de tipo a obj
):
type FinallyBuilder(finallyAction : obj -> unit) =
member x.Bind(v, f) =
try f v
finally finallyAction v
member x.Return(v) = v
let cleanup = FinallyBuilder(printfn "%A")
let res =
cleanup { let! a = new System.Random()
let! b = "hello"
return 3 }
F # me está dando algunos problemas con sus reglas de inferencia de tipos. Estoy escribiendo un generador de computación simple pero no puedo corregir correctamente mis restricciones de tipo genérico.
El código que querría se ve como sigue en C # :
class FinallyBuilder<TZ>
{
readonly Action<TZ> finallyAction;
public FinallyBuilder(Action<TZ> finallyAction)
{
this.finallyAction = finallyAction;
}
public TB Bind<TA, TB>(TA x, Func<TA, TB> cont) where TA : TZ
{ // ^^^^^^^^^^^^^
try // this is what gives me a headache
{ // in the F# version
return cont(x);
}
finally
{
finallyAction(x);
}
}
}
El mejor (pero el código no compilador) que he encontrado para la versión F # hasta ahora es:
type FinallyBuilder<′z> (finallyAction : ′z -> unit) =
member this.Bind (x : ′a) (cont : ′a -> ′b) =
try cont x
finally finallyAction (x :> ′z) // cast illegal due to missing constraint
// Note: '' changed to ′ to avoid bad syntax highlighting here on SO.
Desafortunadamente, no tengo ni idea de cómo traduciría la restricción de tipo where TA : TZ
en el método Bind
. Pensé que debería ser algo así como ′a when ′a :> ′z
, pero al compilador F # no le gusta esto en ninguna parte y siempre termino con una variable de tipo genérico restringida a otra.
¿Podría alguien mostrarme el código F # correcto?
Antecedentes: Mi objetivo es poder escribir un flujo de trabajo personalizado de F # como este:
let cleanup = new FinallyBuilder (fun x -> ...)
cleanup {
let! x = ... // x and y will be passed to the above lambda function at
let! y = ... // the end of this block; x and y can have different types!
}
Sera algo asi
...Bind<''A when ''A :> ''Z>...
Pero déjame codificarlo para asegurarte de que sea exactamente correcto ...
Ah, parece que sería esto:
type FinallyBuilder<''z> (finallyAction : ''z -> unit) =
member this.Bind<''a, ''b when ''a :> ''z> (x : ''a, cont : ''a -> ''b) : ''b =
try cont x
finally finallyAction x //(x :> ''z)// illegal
excepto eso
http://cs.hubfs.net/forums/thread/10527.aspx
señala que F # no hace contraíntas de la forma "T1:> T2" donde ambas son variables de tipo (asume que T1 = T2). Sin embargo, esto podría estar bien para su caso, ¿qué planeaba usar exactamente como ejemplos concretos de Z
? Probablemente exista una solución alternativa simple o algún código menos genérico que satisfaga el escenario. Por ejemplo, me pregunto si esto funciona:
type FinallyBuilder<''z> (finallyAction : ''z -> unit) =
member this.Bind<''b> (x : ''z, cont : ''z -> ''b) : ''b = //''
try cont x
finally finallyAction x
Parece:
type FinallyBuilder<''z> (finallyAction : ''z -> unit) =
member this.Bind<''b> (x : ''z, cont : ''z -> ''b) : ''b = // ''
try cont x
finally finallyAction x
member this.Zero() = ()
[<AbstractClass>]
type Animal() =
abstract Speak : unit -> unit
let cleanup = FinallyBuilder (fun (a:Animal) -> a.Speak())
type Dog() =
inherit Animal()
override this.Speak() = printfn "woof"
type Cat() =
inherit Animal()
override this.Speak() = printfn "meow"
cleanup {
let! d = new Dog()
let! c = new Cat()
printfn "done"
}
// prints done meow woof
Oh, ya veo, pero d
y c
ahora tienen tipo Animal
. Hm, déjame ver si hay alguna inteligencia en mí ...
Bueno, obviamente que puedes hacer
type FinallyBuilder<''z> (finallyAction : ''z -> unit) =
member this.Bind<''a,''b> (x : ''a, cont : ''a -> ''b) : ''b = // ''
try cont x
finally finallyAction (x |> box |> unbox)
member this.Zero() = ()
que tira la seguridad de tipo (lanzará una excepción de lanzamiento en el tiempo de ejecución si la cosa no es finalmente Activa).
O puedes hacer constructores de tipo específico:
type FinallyBuilderAnimal (finallyAction : Animal -> unit) =
member this.Bind<''a,''b when ''a:>Animal>(x : ''a, cont : ''a -> ''b) : ''b = //''
try cont x
finally finallyAction x
member this.Zero() = ()
let cleanup = FinallyBuilderAnimal (fun a -> a.Speak())
Pero creo que estoy fuera de otras ideas inteligentes.