尝试使用F#验证CSV数据

4

我正在尝试理解一些F#代码,但是遇到了问题。

我有一个CSV文件,它的格式如下:

CorrelationId,TagNumber,Description,CreationDate,UpdateDate,Discipline
8D3F96F3-938F-4599-BCA1-66B13199A39A,Test 70-2,Test tag - Ignore,2016-04-05 14:55:23.503,2016-04-05 14:55:23.503,Mechanical
A9FD4B9D-F7A1-4B7D-917F-D633EA0321E3,test-4,A test tag 24,2016-03-23 15:09:54.667,2016-03-30 17:35:29.553,Civil

我正在使用CSV类型提供程序进行读取

open FSharp.Data
type Tag = CsvProvider<"tags.csv">
let readTags (path:string) =
    let tags = Tag.Load(path)

    printfn "%i" ( tags.Rows |> Seq.length )
    let tag = tags.Rows |> Seq.head

我想验证这些行,所以我借鉴了fsharpforfunandprofit铁路导向编程的方法。

type Result<'TSuccess,'TFailure> = 
| Success of 'TSuccess
| Failure of 'TFailure

let bind switchFunction twoTrackInput = 
    match twoTrackInput with
    | Success s -> switchFunction s
    | Failure f -> Failure f

let validateTagName tag = 
    if String.length tag.TagNumber = 0 then Failure "Tag number cannot be empty"
    else Success tag

let validateTagDescription tag  = 
    if String.length tag.Description = 0 then Failure "Tag description cannot be empty"
    else Success tag

但是在验证方法中我遇到了一个问题,我需要使用类型注释来注释函数。但是我不知道应该把它们注释为什么类型。我尝试创建一个新的类型并进行映射,但没有成功。

type tagType = { TagNumber: string; Description: string}

这些函数现在可以正确编译,但我只是把问题搁置了,因为我现在不确定如何从Tag.Row映射到tagType。理想情况下,我希望在不进行任何映射的情况下进行验证。

所有这些应该看起来怎么样?


你可以使用内联函数进行一些诡计,从而允许基于方法的约束。 - John Palmer
2个回答

5

您已经从类型提供程序中获得了Tag类型。使用该特定数据样本,它提供了一个名为Tag.Row的嵌套类型。您可以使用该类型为您的函数添加注释:

let validateTagName (tag : Tag.Row) = 
    if String.length tag.TagNumber = 0 then Failure "Tag number cannot be empty"
    else Success tag

let validateTagDescription (tag : Tag.Row)  = 
    if String.length tag.Description = 0 then Failure "Tag description cannot be empty"
    else Success tag

这些函数可以编译。

哦天啊,在我摸索的过程中,我也遇到过这个问题,只是我忘记在函数参数周围加上括号了。谢谢。 - stimms

3
补充Mark的回答,问题在于按照面向对象编程的方式使用类时,点操作通常需要类型注释, 因为类型推断通常无法确定正在使用的类型。
例如,在这里,x的类型是什么?
let doSomething x = x.Length    // error FS0072: Lookup on object of indeterminate type 

使用附加到模块的函数可以为类型推断提供所需的信息:

let doSomething x = List.length x   // Compiles OK

类型推断通常可以与您定义的记录类型一起使用:
type tagType = { TagNumber: string; Description: string}
let doSomething x =  x.TagNumber // Compiles OK

但在这种情况下,您正在使用由类型提供程序定义的类,因此类型推断效果不佳。

正如Mark所说,最简单的方法是按照他展示的方式使用类型注释。

另一种选择是编写一个从类型提供程序的 Tag 类型到您自己的 MyTag 类型的转换器函数,然后执行转换。

let myTags = tags.Rows |> Seq.map convertToMyTag

将每一行转换为您的类型。当我想要比仅具有字段的简单记录更为复杂的域类型时,我有时会这样做。
在这种情况下,这将是过度设计(而且您仍然需要向转换器函数添加注释!)
最后,这里有两篇可能有用的文章:理解类型推断排除常见编译器错误

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接