具有至少一个元素的F#序列

时间:2018-07-03 09:31:13

标签: f#

此处是F#的初学者

我想创建一个类型,它是具有至少一个元素的另一种具体类型(事件)的序列。以后可以随时添加任何其他元素。通常,在C#中,我将使用私有List 和公共方法创建一个类。

但是我想使用功能性方法而不是模仿C#方法。或至少尝试一下。

我的思路:

  • 让我们创建一个“ seq”类型,并为它提供一个需要Event类型实例的构造函数

    type Event = Event of string
    
    type PublishedEvents = EventList of seq<Event> with
        static member create (event:Event) = EventList(Seq.singleton event)
    
  • 现在,我们添加一个“ add”方法来添加另一个可选的Event实例

    type PublishedEvents with
        member this.add(event:Event) = Seq.append this [event]
    

但这不起作用,F#抱怨“ this”与seq <'a>不兼容。

所以我尝试了这个:

type PublishedEvents with
    member this.add (event:Event) : PublishedEvents = EventList(Seq.append this [event])

现在,它抱怨“此”与seq 不兼容......现在使我感到困惑,因为它上面的几行显示EventList of seq<Event> ...所以我想我需要以某种方式转换{{ 1}}返回EventList,这样我就可以使用seq<Event>吗?

Seq.append

但是我不知道该怎么做。

我什至朝着正确的方向前进吗?模仿带有支持字段的C#类是否更好?还是我错过了什么?

2 个答案:

答案 0 :(得分:5)

事件的实际顺序包装在一个EventList区分的联合案例中。

您可以打开包装,然后重新包装,如下所示:

type PublishedEvents with
    member this.add(event:Event) =
        match this with
        | EventList events -> Seq.append events [event] |> EventList

但是,如果只是一个PublishedEvents案例,其中包含一个序列,需要您反复包装和拆包值,那么我首先要质疑创建此EventList类型的价值。 / p>

此外,请注意,此add方法不会更改现有的PublishedEvents。由于Seq.append的工作方式,它创建了一个具有新事件序列的新事件,因为seq<'a>实际上只是System.Collections.Generic.IEnumerable<'a>)的F#名称。

此外,您的方法不会阻止创建非空事件序列。 EventListPublishedEvents的公共构造函数,因此您可以编写:

EventList []

使类型系统强制执行非空序列的一种简单方法是:

type NonEmptySeq<'a> = { Head : 'a; Tail : seq<'a> } with
    static member Create (x:'a) = { Head = x; Tail = [] }
    member this.Add x = { this with Tail = Seq.append this.Tail [x] }

let a = NonEmptySeq.Create (Event "A")
let b = a.Add (Event "B")

但同样,这些序列是不可变的。如果需要突变,可以使用C#List<'a>进行类似的操作。在F#中,它称为ResizeArray<'a>

type NonEmptyResizeArray<'a> = { Head : 'a; Tail : ResizeArray<'a> } with
    static member Create (x:'a) = { Head = x; Tail = ResizeArray [] }
    member this.Add x = this.Tail.Add x

let a = NonEmptyResizeArray.Create (Event "A")
a.Add (Event "B")

答案 1 :(得分:5)

我建议您增加功能,而不是为您的类型创建成员-在功能中完成它。例如,这将达到相同的效果,我认为这是更惯用的F#:

type Event = Event of string
type PublishedEvents = EventList of Event * Event list

let create e = EventList (e,[])
let add (EventList(head,tail)) e = EventList(e,head::tail)
let convert (EventList(head,tail)) = head::tail |> Seq.ofList

let myNewList = create (Event "e1")
let myUpdatedList = add myNewList (Event "e2")
let sequence = convert myUpdatedList
  val序列:seq = [事件“ e2”;事件“ e1”]

另一方面,如果您的目标是与C#互操作,则在C#方面更容易使用该方法。