2009-12-12 143 views
1

试图学习一些F#,我遇到了几个hangups。F#铸造和泛型

下面的代码:

#light 
module HtmlModule 

type HtmlString(text:string) = 
    override x.ToString() = text 

type HtmlAttribute(key:string, value:string) = 
    inherit HtmlString(key + "=\"" + value + "\""); 

type HtmlElement(tag: string, ?contents:list<'a> when 'a :> HtmlString) = 
    inherit HtmlString("") 
    let innerContents = defaultArg contents [] 
    let castToHtmlAttribute (hs:HtmlString) : Option<HtmlAttribute> = 
     match hs with 
     | :? HtmlAttribute as temp -> Some(temp) 
     | _ -> None 
    member x.tag = tag 
    member x.contents = innerContents |> List.filter(fun x -> (castToHtmlAttribute x).IsNone) 
    member x.attributes = innerContents |> List.map(fun x -> (castToHtmlAttribute x)) |> List.filter(fun x-> x.IsSome) |> List.map(fun x->x.Value) 

    override x.ToString() = 
     let sb = System.Text.StringBuilder() 
     sb.Append("<" + x.tag) 

     for att in x.attributes do 
      sb.Append(" " + att.ToString()) 

     sb.Append(">") 

     for item in x.contents do 
      sb.Append(item.ToString()) 

     sb.Append("</" + x.tag + ">") 
     sb.ToString() 



let element tag contents = new HtmlElement(tag, contents) 
let div contents = element "div" contents 
let p contents = element "p" contents 
let text text = new HtmlString(text) 
let attr key value = new HtmlAttribute(key, value) 
let classes value = attr "class" value 

和快速的控制台程序,以显示该问题:

#light 

open HtmlModule 


let stuff = [for a in 1 .. 10 do yield p [text ("some String " + a.ToString())]] 

let elem = div [ 
      attr "class" "someClass"; 
      text "This is some inner text"; 
      div stuff; 
     ] 

let result = elem.ToString() 
printfn "%A" result 

编译器将不允许行

div stuff; 

它指出:

"Error 7 Type mismatch. Expecting a HtmlString list but given a HtmlElement list The type 'HtmlString' does not match the type 'HtmlElement'"

这个问题可以跟踪到castToHtmlAttribute方法签名

let castToHtmlAttribute (hs:HtmlString) : Option<HtmlAttribute> = 

如果我做像这样这条线比较通用:

let castToHtmlAttribute (hs:'a when 'a :> HtmlString) : Option<HtmlAttribute> = 

我得到一个新的错误:

"Error 2 This runtime coercion or type test from type 'a to HtmlAttribute involves an indeterminate type based on information prior to this program point. Runtime type tests are not allowed on some types. Further type annotations are needed."

任何帮助表示赞赏。

+1

贾斯汀,看看这篇文章:http://stackoverflow.com/questions/1883246/none-pure-functional-code-smells/1884256#1884256 :)好吧,与被说,我会建议重写你的数据模型是这样的:http://pastebin.com/f27045779 – Juliet 2009-12-12 19:56:38

+0

感谢您的链接,这有助于很多。我有点认为我做的事情完全错了。 :) – justin 2009-12-12 20:28:12

回答

3

这里的诊断不是很好。

您可以修复的代码如下所示:

let stuff = [for a in 1 .. 10 do 
       yield (p [text ("some String " + a.ToString())]) :> HtmlString] 

在那里我已经插入了显式上溯造型为基本类型。

+0

我真的想要更多的API,而不是客户端代码不必投。 – justin 2009-12-12 19:49:53

+1

请考虑朱丽叶的建议。总的来说,在大多数语言中很难编写涉及列表和列表的代码,并且无需偶尔显式投射。 – Brian 2009-12-12 20:09:30

1

Expecting a HtmlString list but given a HtmlElement list The type 'HtmlString' does not match the type 'HtmlElement'"

这说明问题很好。对于F#,List<HTMLElement>不是List<HTMLString>,比如List<Orange>List<Fruit>

如果是这样,你可以写

Apple :: oranges 

因为这没有任何意义,F#希望你投垂头丧气明确。请告诉div使用通用列表。

总之,您可以将类型HTMLString list更改为多态性#HTMLString list或手动转换参数或stuff