lau*_*n91 4 .net f# infix-notation infix-operator
我想要做的是使用infix fmap(我已经定义为<^>)来处理多种类型,例如Option和Either(自定义类型).
鉴于:
type Either<'a, 'b> = Left of 'a | Right of 'b
Run Code Online (Sandbox Code Playgroud)
在代码中我希望能够做到:
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
Run Code Online (Sandbox Code Playgroud)
每个地方(<^>):
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
Run Code Online (Sandbox Code Playgroud)
要使Option <^>工作,我已经扩展了模块:
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 ()
Run Code Online (Sandbox Code Playgroud)
并为以下两者:
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
Run Code Online (Sandbox Code Playgroud)
这几乎可以工作,但一次只能使用一个.一个Either模块也可以附加到FSharp.Core,但同样你只能有一个或另一个.
我知道这可以用2种自定义类型完成,例如Either和Maybe(Haskell选项),但我想坚持使用Option.
任何和所有建议欢迎.
这实际上并不容易在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
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用具有静态解析类型参数的函数来根据类型选择适当的方法:
let inline fmap (f : ^c -> ^d ) (x : ^a) =
((^b or ^a) : (static member FMap : ^b * ( ^c -> ^d ) * ^a -> ^e ) (Functor, f, x))
Run Code Online (Sandbox Code Playgroud)
注意^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
Run Code Online (Sandbox Code Playgroud)
对于运算符表单,只需定义:
let inline (<^>) f x = fmap f x
Run Code Online (Sandbox Code Playgroud)
最终定义了函数:
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)
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用<^>运算符执行此类操作:
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)
Run Code Online (Sandbox Code Playgroud)
您还可以查看F#+,以更全面地实现许多这些标准功能抽象.
| 归档时间: |
|
| 查看次数: |
490 次 |
| 最近记录: |