FSharpPlus库以及它如何模拟一些通用类型类给我留下了深刻的印象。但是我真的相信将类型类实例与类型定义分离的力量,因此我一直在尝试尝试做一些与FSharpPlus相同的技术,但是将类型类实例视为完全独立的概念证明实体。老实说,我的能力超出了SRTP的能力,FSharpPlus的源代码让我有些头疼。到目前为止,这是我的尝试:
module Monad =
let inline bind (m:^Monad) (f:'a -> '``Monad<'b>``) (x:'``Monad<'a>``) : '``Monad<'b>`` =
(^Monad : (member Bind : _*_ -> _) (m, f, x))
let inline rtn (m:^Monad) (source:'a) : '``Monad<'a>`` =
(^Monad : (member Return : _ -> _) (m, source))
type ListMonad = ListMonad with
member this.Bind (f, x) = List.collect f x
member this.Return x = [x]
type OptionMonad = OptionMonad with
member this.Bind (f, x) = Option.bind f x
member this.Return x = Some x
这使我可以做一些基本的事情,但是一旦我在单个函数中尝试两次使用bind
时,我就会碰壁。
let inline applyFromMonad (monadInstance) (f:'``Monad<'a -> 'b>) (x:'``Monad<'a>``) : '``Monad<'b>`` =
f |> Monad.bind monadInstance (fun f' ->
x |> Monad.bind monadInstance (fun a' ->
Monad.rtn monadInstance (f' a')))
(稍后我将担心实现计算表达式)。问题在于,编译器期望f'
和a'
是相同的类型,大概是因为Monad.bind
的通用性不够。并不是将bind函数的参数f
视为接受任何通用类型'a的函数,而是将其视为applyFromMonad
函数中接受某些具体类型的函数。是否有办法解决,以便bind
两次调用时可以被赋予不同类型的参数?
当然,如果this pull request成为语言,这点毫无意义,但我没有足够的见识来知道这种情况是否会很快出现。也许我的回答是等待此功能...
当然,如果还有另一种方法可以实现相同的目标(类型类具有与类型定义不同的类型类实例,并且其签名通常需要类型较高的类型才能正确定义),那么我很乐意考虑一下不同。