F#中最常用的处理方式是什么?假设我有一个属性,我想要一个类型来满足在实例级别上没有意义,但理想情况下我希望有一些模式匹配可用吗?
为了使这更具体,我已经定义了一个表示环概念的界面(在抽象代数意义上)。我会选择:
1
// Misses a few required operations for now
type IRing1<'a when 'a: equality> =
abstract member Zero: 'a with get
abstract member Addition: ('a*'a -> 'a) with get
让我们假设我这样使用它:
type Integer =
| Int of System.Numerics.BigInteger
static member Zero with get() = Int 0I
static member (+) (Int a, Int b) = Int (a+b)
static member AsRing
with get() =
{ new IRing1<_> with
member __.Zero = Integer.Zero
member __.Addition = Integer.(+) }
允许我写下这样的内容:
let ring = Integer.AsRing
然后让我很好地使用我为验证环的属性而编写的单元测试。但是,我无法对此进行模式匹配。
2
type IRing2<'a when 'a: equality> =
abstract member Zero: 'a with get
abstract member Addition: ('a*'a -> 'a) with get
type Integer =
| Int of System.Numerics.BigInteger
static member Zero with get() = Int 0I
static member (+) (Int a, Int b) = Int (a+b)
interface IRing2<Integer> with
member __.Zero = Integer.Zero
member __.Addition with get() = Integer.(+)
现在我可以模式匹配,但这也意味着我可以编写废话,例如
let ring = (Int 3) :> IRing2<_>
3
我可以使用额外的间接级别并基本上定义
type IConvertibleToRing<'a when 'a: equality>
abstract member UnderlyingTypeAsRing : IRing3<'a> with get
然后基本上构建IRing3&lt; _&gt;与#1下的方式相同。 这会让我写一下:
let ring = ((Int 3) :> IConvertibleToRing).UnderlyingTypeAsRing
这是冗长的,但至少我写的东西不再是废话。然而,除了详细程度之外,所获得的额外复杂程度并不是真正的“感觉”和#34;这里有道理。
4
我还没有完全想到这个,但我可以只有一个Integer类型而不实现任何接口,然后是一个名为Integer的模块,它允许Ring接口的绑定值。我想我可以在辅助函数中使用反射,该函数为任何类型创建任何IRing实现,其中还有一个具有相同名称的模块(但其中有一个模块后缀的编译名称)可用?这将结合#1和#2的好处我猜,但我不确定它是否可能和/或过于做作?
仅仅是为了背景:只是为了它,我试图在F#中实现我自己的迷你计算机代数系统(例如Mathematica或Maple),我想我会遇到足够的代数结构来启动引入诸如IRing之类的接口进行单元测试以及(可能)稍后处理这些代数结构的一般操作。
我意识到这里有什么或不可能的部分更多地与限制如何在.NET而不是F#中完成任务有关。如果我的意图足够明确,我很乐意在评论中评论其他函数式语言如何解决这类设计问题。
答案 0 :(得分:1)
关于如何在其他函数式语言中实现Rings的问题,在Haskell中,您通常会定义一个包含所有Ring操作的Type Class Ring。
在F#中没有类型类,但是你可以使用内联和重载更接近:
module Ring =
type Zero = Zero with
static member ($) (Zero, a:int) = 0
static member ($) (Zero, a:bigint) = 0I
// more overloads
type Add = Add with
static member ($) (Add, a:int ) = fun (b:int ) -> a + b
static member ($) (Add, a:bigint) = fun (b:bigint) -> a + b
// more overloads
type Multiply = Multiply with
static member ($) (Multiply, a:int ) = fun (b:int ) -> a * b
static member ($) (Multiply, a:bigint) = fun (b:bigint) -> a * b
// more overloads
let inline zero() :'t = Zero $ Unchecked.defaultof<'t>
let inline (<+>) (a:'t) (b:'t) :'t= (Add $ a) b
let inline (<*>) (a:'t) (b:'t) :'t= (Multiply $ a) b
// Usage
open Ring
let z : int = zero()
let z': bigint = zero()
let s = 1 <+> 2
let s' = 1I <+> 2I
let m = 2 <*> 3
let m' = 2I <*> 3I
type MyCustomNumber = CN of int with
static member ($) (Ring.Zero, a:MyCustomNumber) = CN 0
static member ($) (Ring.Add, (CN a)) = fun (CN b) -> CN (a + b)
static member ($) (Ring.Multiply, (CN a)) = fun (CN b) -> CN (a * b)
let z'' : MyCustomNumber = zero()
let s'' = CN 1 <+> CN 2
如果您希望使用此方法进行扩展,可以查看已使用Zero(Mempty)和Add(Mappend)定义FsControl的Monoid。您可以提交Ring的拉取请求。
现在,如果您计划仅将所有这些与数字一起使用,为什么不在F#中使用GenericNumbers,(+)
和(*)
已经是通用的,那么您有LanguagePrimitives.GenericZero
和{{ 1}}。