F#针对多种类型的公共中缀运算符(fmap,applicative,bind等)

时间:2017-08-17 09:38:00

标签: .net f# infix-notation infix-operator

我想要做的是使用infix fmap(我已经定义为< ^>)来处理多种类型,例如Option和Either(自定义类型)。

假设:

   type Either<'a, 'b> = Left of 'a | Right of 'b 

在代码中我希望能够:

   let fO (a : int option) = None
   let fE (a : Either<string,int>) = Left "dummy"

   let mO = Some 1
   let mE = Right 1

   let testO = f0 <^> m0
   let testE = fE <^> mE

每个(&lt; ^&gt;):

    let (<^>) f m = match m with | Some a -> Some <| f a | None -> None
    let (<^>) f m = match m with | Right a -> Right <| f a | Left a -> Left a

获取选项&lt; ^&gt;工作我扩展了模块:

    namespace Microsoft.FSharp.Core
    [<AutoOpen>]
    module Option =
    let (<^>) f m = match m with | Some a -> Some <| f a | None -> None

    [<assembly:AutoOpen("Microsoft.FSharp.Core")>]
    do ()

对于Either:

    type Either<'a, 'b> = Left of 'a | Right of 'b with
       static member (<^>) (f,m) = match m with | Right a -> Right <| f a | Left a -> Left a

这几乎可以使用,但一次只能使用一个。 一个Either模块也可以附加到FSharp.Core,但同样你只能拥有一个或另一个。

我知道这可以用2种自定义类型完成,例如Either和Maybe(Haskell选项),但我想坚持选择。

欢迎提出任何建议。

2 个答案:

答案 0 :(得分:7)

这并不是F#中容易表达的东西,唯一的方法是使用静态解析的类型参数,通常不会被认为是惯用的。

对于新的自定义类型,这样做非常简单,但将其改造为现有类型则更为复杂。支持两者再次稍微困难。

您可以继续的方法是使用针对现有类型硬编码的静态方法创建单个案例区分联合的帮助器类型:

type Functor = Functor
    with 
    static member FMap (Functor, mapper : 'T -> 'U, opt : Option<'T>) : Option<'U> =
        Option.map mapper opt
    static member FMap (Functor, mapper : 'T -> 'U, ch : Choice<'T, _>) : Choice<'U, _> =
        match ch with
        |Choice1Of2 v -> Choice1Of2 (mapper v)
        |Choice2Of2 v -> Choice2Of2 v

现在,您可以使用具有静态解析类型参数的函数,根据类型选择合适的方法:

let inline fmap (f : ^c -> ^d ) (x : ^a) =
    ((^b or ^a) : (static member FMap : ^b * ( ^c -> ^d ) * ^a -> ^e ) (Functor, f, x))

注意^b or ^a条件?这也为我们提供了一种将此行为插入自定义类型的方法。

type Either<'a, 'b> = Left of 'a | Right of 'b with
    static member FMap (Functor, f, m) = 
        match m with | Right a -> Right <| f a | Left a -> Left a

对于运算符表单,只需定义:

let inline (<^>) f x = fmap f x

您最终定义了函数:

val inline fmap :
  f:( ^c ->  ^d) -> x: ^a ->  ^e
    when (Functor or  ^a) : (static member FMap : Functor * ( ^c ->  ^d) *  ^a ->  ^e)
val inline ( <^> ) :
  f:( ^a ->  ^b) -> x: ^c ->  ^d
    when (Functor or  ^c) : (static member FMap : Functor * ( ^a ->  ^b) *  ^c ->  ^d)

现在,您可以使用<^>运算符执行此类操作:

let x  = (fun x -> x + 1) <^> (Some 1)
let x'  = (fun x -> x + 1) <^> (None)
let z<'a> : Either<'a, _> = (fun x -> x + 2) <^> (Right 2)
let z'  = (fun x -> x + 2) <^> (Left 5)

另外,您可以查看F#+以更全面地实现许多标准功能抽象。

答案 1 :(得分:0)

为了完整性,最终实施

public void onConfigurationChanged(Configuration newConfig) {
    int orientation = getResources().getConfiguration().orientation;
}