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

Mir*_*rek 5 f#

F#的初学者在这里

我想创建一个类型,它是另一个具有至少一个元素的具体类型(Event)的序列.任何其他元素都可以在以后随时添加.通常在C#中我会创建一个带有私有List <Event>和公共方法的类.

但我想用功能方法来做,而不是模仿C#方法.或者至少尝试一下.

我的思路:

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

    type Event = Event of string
    
    type PublishedEvents = EventList of seq<Event> with
        static member create (event:Event) = EventList(Seq.singleton event)
    
    Run Code Online (Sandbox Code Playgroud)
  • 现在让我们添加一个"添加"方法来添加另一个可选的Event实例

    type PublishedEvents with
        member this.add(event:Event) = Seq.append this [event]
    
    Run Code Online (Sandbox Code Playgroud)

但是这不起作用,F#抱怨"这个"与seq <'a>不兼容.

所以我尝试了这个:

type PublishedEvents with
    member this.add (event:Event) : PublishedEvents = EventList(Seq.append this [event])
Run Code Online (Sandbox Code Playgroud)

现在它抱怨"这个"与seq <Event>不兼容...这让我感到困惑,因为它上面几行说EventList of seq<Event>...所以我想我需要以某种方式转换EventList回来,seq<Event>以便我可以使用Seq.append

let convertFunction (eventList:PublishedEvents) : seq<Event> = ???
Run Code Online (Sandbox Code Playgroud)

但我不知道该怎么做.

我是否正朝着正确的方向前进?是否更好地模仿具有支持字段的C#类?或者我错过了什么?

The*_*Fox 6

实际的事件序列包含在一个有EventList区别的联合案例中.

您可以打开它并重新包装它,如下所示:

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

但是,我必须首先质疑创建此PublishedEvents类型的价值,如果它只是一个EventList包含需要重复包装和展开值的序列的单个案例.

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

此外,您的方法不会阻止创建非空事件序列.EventList是一个公共构造函数,PublishedEvents所以你可以写:

EventList []
Run Code Online (Sandbox Code Playgroud)

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

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")
Run Code Online (Sandbox Code Playgroud)

但同样,这些序列是不可改变的.List<'a>如果你需要变异,你可以用C#做​​类似的事情.在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")
Run Code Online (Sandbox Code Playgroud)


Dev*_*ewb 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
Run Code Online (Sandbox Code Playgroud)

val序列:seq = [事件"e2"; 活动"e1"]

另一方面,如果你的目标是与C#互操作,你的方法将更容易在C#方面消费.