在F#中扩展Core.Option模块

时间:2013-04-18 08:38:07

标签: f#

我想扩展其中一个现有的“核心”模块,例如Core.Option

module Microsoft.FSharp.Core.Option

    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

(我知道bind函数,但我认为在某些情况下filter选项的方法更方便。

但不幸的是,如果没有明确地打开filter命名空间,我就无法使用Microsoft.FSharp.Core方法:

// Commenting following line will break the code!
open Microsoft.FSharp.Core

let v1 = Some 42
let v2 = v1 |> Option.filter (fun v -> v > 40)

printfn "v2 is: %A" v2

在大多数情况下,如果不打开适当的命名空间,我们就无法使用模块中的函 F#编译器自动“打开”一些预定义(核心)命名空间(如Microsoft.FSharp.Core),这不会从“模块扩展”引入范围方法,我们仍然应该手动打开核心命名空间。

我的问题是:有任何解决方法吗?

或者扩展“核心”模块的最佳方法是在自定义命名空间中创建此类扩展并手动打开此命名空间?

// Lets custom Option module in our custom namespace
module CustomNamespace.Option

    let filter predicate op = ...

// On the client side lets open our custom namespace.
// After that we can use both Option modules simultaneously!
open CustomNamespace

let v1 = Some 42
let b = 
    v1 |> Option.filter (fun v -> v > 40) // using CustomNamespace.Option
    |> Option.isSome // using Microsoft.FSharp.Core.Option

4 个答案:

答案 0 :(得分:5)

如果将AutoOpen属性添加到模块中会有帮助吗?

[<AutoOpen>]
module Microsoft.FSharp.Core.Option

    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

修改

这样可行,但仅适用于装配边框。它在同一个程序集中不起作用:

namespace Microsoft.FSharp.Core
module Option =
    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

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

从另一个集会中调用它:

[<EntryPoint>]
let main args = 
    let f () = Some "" |> Option.filter (fun f -> true)
    Console.WriteLine("Hello world!")
    0

答案 1 :(得分:5)

对于生产代码,我会按照Taha的回答建议:创建自己的模块并根据需要打开/别名。你作为程序员的大部分时间都将花在阅读代码上。阅读F#代码可能会非常令人沮丧,因为不清楚方法的来源。

话虽如此,我很惊讶地发现这有效:

namespace Microsoft.FSharp.Core

module Option =

    let filter predicate op =
        match op with
        | Some(v) -> if predicate(v) then Some(v) else None
        | None -> None

namespace USERCODE

module Option = Microsoft.FSharp.Core.Option

module M =

    let test () =
        Some 1
        |> Option.filter (fun x -> x > 0)
        |> Option.map (fun x -> x + 1)

它不会消除在文件头部写入内容的需要,但它确实需要打开命名空间。与Microsoft.FSharp.Core无关,因为默认情况下它始终处于打开状态,但对其他命名空间很有帮助。

答案 2 :(得分:4)

为了扩展F#模块,创建另一个具有相同名称的模块:

module Option =

    let filter predicate op =
        match op with
        | Some v  -> match predicate v with true -> Some v | false -> None
        | None    -> None


let v1 = Some 42
let v2 = v1 |> Option.filter (fun v -> v > 40)

printfn "v2 is: %A" v2

答案 3 :(得分:0)

根据docs,在扩展类型时应该使用类似的方法。但是,我肯定出了点问题,因为它无法编译并给出。以下错误:

  

此类型扩展的一个或多个声明的类型参数   缺少或错误的类型约束与原始类型不匹配   选项<_>'F#编译器(957)的约束

open System.Runtime.CompilerServices

[<Extension>]
type Option with
    [<Extension>]
    static member fromBool predicate word =
        if predicate word
        then Some word
        else None

这不是一个有效的答案,更多的是对原始问题的补充。不确定是否值得打开新线程?