有没有办法对 do! 进行不同的实现?让!在计算表达式中?

发布于 2024-10-30 18:26:57 字数 631 浏览 1 评论 0原文

我需要不同的行为来做!让!在我的自定义计算表达式中。

我尝试通过以下方式实现此目的:

type FooBuilder() = class
    member b.Bind<'T, 'U>(x:'T, f:unit->'U):'U = failwith "not implemented" //do! implementation
    member b.Bind<'T, 'U>(x:'T, f:'T->'U):'U = failwith "not implemented" //let! implementation
    member b.Return<'T>(x:'T):'T = failwith "not implemented" //return implementation
end

let foo = FooBuilder()
let x = foo {
    do! ()
    return 2
}

但是编译器给了我一个错误:

无法根据此程序点之前的类型信息确定方法“Bind”的唯一重载。可用的重载如下所示(或在“错误列表”窗口中)。可能需要类型注释。

有没有办法有不同的实现 do!并让!?

I need a different behavior for do! and let! in my custom computation expression.

I try to achieve this in the following way:

type FooBuilder() = class
    member b.Bind<'T, 'U>(x:'T, f:unit->'U):'U = failwith "not implemented" //do! implementation
    member b.Bind<'T, 'U>(x:'T, f:'T->'U):'U = failwith "not implemented" //let! implementation
    member b.Return<'T>(x:'T):'T = failwith "not implemented" //return implementation
end

let foo = FooBuilder()
let x = foo {
    do! ()
    return 2
}

But compiler gives me an error:

A unique overload for method 'Bind' could not be determined based on type information prior to this program point. The available overloads are shown below (or in the Error List window). A type annotation may be needed.

Is there a way to have a different implementation of do! and let!?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

秋风の叶未落 2024-11-06 18:26:57

如果您想保持 let! 中使用的 Bind 操作通用,那么就没有办法说 F# 在翻译 do! (重载必然必须重叠)。

一般来说,如果您希望 let!do! 获得不同的行为,那么这表明您的计算表达式可能定义不正确。这个概念非常灵活,它可以用于更多的事情,而不仅仅是声明 monad,但你可能把它延伸得太远了。如果您可以写下有关您想要实现的目标的更多信息,那将会很有用。无论如何,这里有一些可能的解决方法...

您可以添加一些额外的包装并编写类似 do! 的内容。换行<|表达式

type Wrapped<'T> = W of 'T
type WrappedDo<'T> = WD of 'T

type FooBuilder() = 
  member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = failwith "let!" 
  member b.Bind<'T, 'U>(x:WrappedDo<unit>, f:unit->'U):'U = failwith "do!"
  member b.Return<'T>(x:'T):Wrapped<'T> = failwith "return"

let wrap (W a) = WD a
let bar arg = W arg

let foo = FooBuilder()

// Thanks to the added `wrap` call, this will use the second overload
foo { do! wrap <| bar()
      return 1 }

// But if you forget to add `wrap` then you still get the usual `let!` implementation
foo { do! wrap <| bar()
      return 1 }

另一种选择是使用动态类型测试。这有点低效(而且有点不优雅),但它可能会起作用,具体取决于您的场景:

member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = 
  if typeof<'T> = typeof<unit> then 
    failwith "do!" 
  else 
    failwith "let!" 

但是,当您编写 let! 时,这仍然会使用 do! 重载。 () = 条

If you want to keep the Bind operation used in let! generic, then there is no way to say that F# should use different implementation when translating do! (the overloads will necessarily have to overlap).

In general, if you want to get different behavior for let! and for do! then it suggests that your computation expression is probably incorrectly defined. The concept is quite flexible and it can be used for more things than just for declaring monads, but you may be stretching it too far. If you can write more information about what you want to achieve, that would be useful. Anyway, here are some possible workarounds...

You can add some additional wrapping and write something like do! wrap <| expr.

type Wrapped<'T> = W of 'T
type WrappedDo<'T> = WD of 'T

type FooBuilder() = 
  member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = failwith "let!" 
  member b.Bind<'T, 'U>(x:WrappedDo<unit>, f:unit->'U):'U = failwith "do!"
  member b.Return<'T>(x:'T):Wrapped<'T> = failwith "return"

let wrap (W a) = WD a
let bar arg = W arg

let foo = FooBuilder()

// Thanks to the added `wrap` call, this will use the second overload
foo { do! wrap <| bar()
      return 1 }

// But if you forget to add `wrap` then you still get the usual `let!` implementation
foo { do! wrap <| bar()
      return 1 }

Another alternative would be to use dynamic type tests. This is a bit inefficient (and a bit inelegant), but it may do the trick, depending on your scenario:

member b.Bind<'T, 'U>(x:Wrapped<'T>, f:'T->'U):'U = 
  if typeof<'T> = typeof<unit> then 
    failwith "do!" 
  else 
    failwith "let!" 

However, this would still use the do! overload when you write let! () = bar.

梦冥 2024-11-06 18:26:57

您可以尝试其他方法,有点难看,但应该可以工作:

let bindU (x, f) = f x // you must use x, or it'll make the Bind method less generic.
let bindG (x, f) = f x
member b.Bind(x : 'a, f : 'a -> 'b) =
    match box x with
    | :? unit -> bindU (x, f)
    | _ -> bindG (x, f)

它装箱 a (将其转换为 obj )并检查它是否为unit 类型,然后重定向到正确的过载。

You could try something else, a bit ugly, but should work:

let bindU (x, f) = f x // you must use x, or it'll make the Bind method less generic.
let bindG (x, f) = f x
member b.Bind(x : 'a, f : 'a -> 'b) =
    match box x with
    | :? unit -> bindU (x, f)
    | _ -> bindG (x, f)

It boxes a (converts it to obj) and checks if it is of type unit, then redirects to the correct overload.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文