F#中字符串的编译时限制,类似于度量单位 - 是否可能?

byt*_*ter 15 string f# units-of-measurement

我正在使用F#开发Web应用程序.考虑保护用户输入字符串免受SQL,XSS和其他漏洞的影响.

换句话说,我需要一些编译时约束,这些约束允许我将纯字符串与表示SQL,URL,XSS,XHTML等的字符串区分开来.

许多语言都有它,例如Ruby的原生字符串插值功能#{...}.
使用F#,似乎度量单位表现非常好,但它们仅适用于数字类型.
有几种解决方案采用运行时 UoM (链接),但我认为这是我的目标的开销.

我查看了FSharpPowerPack,似乎很有可能为字符串提出类似的东西:

[<MeasureAnnotatedAbbreviation>] type string<[<Measure>] 'u> = string
// Similarly to Core.LanguagePrimitives.IntrinsicFunctions.retype
[<NoDynamicInvocation>]
let inline retype (x:'T) : 'U = (# "" x : 'U #)
let StringWithMeasure (s: string) : string<'u> = retype s

[<Measure>] type plain
let fromPlain (s: string<plain>) : string =
    // of course, this one should be implemented properly
    // by invalidating special characters and then assigning a proper UoM
    retype s

// Supposedly populated from user input
let userName:string<plain> = StringWithMeasure "John'); DROP TABLE Users; --"
// the following line does not compile
let sql1 = sprintf "SELECT * FROM Users WHERE name='%s';" userName
// the following line compiles fine
let sql2 = sprintf "SELECT * FROM Users WHERE name='%s';" (fromPlain userName)
Run Code Online (Sandbox Code Playgroud)

注意:这只是一个样本; 不建议使用SqlParameter.:-)

我的问题是:有一个体面的图书馆吗?是否有可能添加语法糖?
谢谢.

更新1:我需要编译时限制,谢谢Daniel.

更新2:我试图避免任何运行时开销(元组,结构,有区别的联合等).

Ram*_*nir 7

有点晚了(我确定有一种时间格式,在2月23日到11月30日之间只有一点不同),我相信这些单行可以兼容你的目标:

type string<[<Measure>] 'm> = string * int<'m>

type string<[<Measure>] 'm> = { Value : string }

type string<[<Measure>] 'm>(Value : string) = struct end
Run Code Online (Sandbox Code Playgroud)


Dan*_*iel 2

很难说出你想做什么。您说您“需要一些运行时约束”,但您希望使用严格的编译时测量单位来解决这个问题。我认为简单的解决方案是创建验证其输入的SafeXXXString类(其中XXXSqlXml)。

type SafeSqlString(sql) =
  do
    //check `sql` for injection, etc.
    //raise exception if validation fails
  member __.Sql = sql
Run Code Online (Sandbox Code Playgroud)

它为您提供运行时安全性,而不是编译时安全性。但它很简单、自记录,并且不需要阅读 F# 编译器源代码即可工作。

但是,为了回答你的问题,我没有看到任何方法可以用度量单位来做到这一点。就语法糖而言,您也许可以将其封装在一个 monad 中,但我认为这会使其变得更加笨重,而不是更少。