F#中的SRTP类型类

时间:2019-03-16 23:42:57

标签: f#

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成为语言,这点毫无意义,但我没有足够的见识来知道这种情况是否会很快出现。也许我的回答是等待此功能...

当然,如果还有另一种方法可以实现相同的目标(类型类具有与类型定义不同的类型类实例,并且其签名通常需要类型较高的类型才能正确定义),那么我很乐意考虑一下不同。

0 个答案:

没有答案