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#类?或者我错过了什么?
实际的事件序列包含在一个有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)
我建议您更加实用,而不是为您的类型创建成员 - 在您的功能中完成它.例如,这将实现相同,我认为它更惯用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#方面消费.