F# Inline Function Specialization - function

My current project involves lexing and parsing script code, and as such I'm using fslex and fsyacc. Fslex LexBuffers can come in either LexBuffer<char> and LexBuffer<byte> varieties, and I'd like to have the option to use both.
In order to user both, I need a lexeme function of type ^buf -> string. Thus far, my attempts at specialization have looked like:
let inline lexeme (lexbuf: ^buf) : ^buf -> string where ^buf : (member Lexeme: char array) =
new System.String(lexbuf.Lexeme)
let inline lexeme (lexbuf: ^buf) : ^buf -> string where ^buf : (member Lexeme: byte array) =
System.Text.Encoding.UTF8.GetString(lexbuf.Lexeme)
I'm getting a type error stating that the function body should be of type ^buf -> string, but the inferred type is just string. Clearly, I'm doing something (majorly?) wrong.
Is what I'm attempting even possible in F#? If so, can someone point me to the proper path?
Thanks!

Functions and members marked as inline cannot be overloaded, so your original strategy won't work. You need to write different code for both of the declarations, so you need to use overloading (if you want to write this without boxing and dynamic type tests).
If you're using standard F# tools, then the type you'll get as a buffer will always be LexBuffer<'T> and you want to have two overloads based on the type argument. In this case, you don't need the static member constraints at all and can write just:
type Utils =
static member lexeme(buf:LexBuffer<char>) =
new System.String(buf.Lexeme)
static member lexeme(buf:LexBuffer<byte>) =
System.Text.Encoding.UTF8.GetString(buf.Lexeme)

Are you sure this strategy of redefining inline functions with different argument types can work? Looks like you're trying to overload to me...

type LexBuffer<'a>(data : 'a []) =
member this.Lexeme = data
let lexeme (buf : LexBuffer<'a>) =
match box buf.Lexeme with
| :? (char array) as chArr ->
new System.String(chArr)
| :? (byte array) as byArr ->
System.Text.Encoding.UTF8.GetString(byArr)
| _ -> invalidArg "buf" "must be either char or byte LexBuffer"
new LexBuffer<byte>([| 97uy; 98uy; 99uy |])
|> lexeme
|> printfn "%A"
new LexBuffer<char>([| 'a'; 'b'; 'c' |])
|> lexeme
|> printfn "%A"

Related

Unable to define a parser in Haskell: Not in scope: type variable ‘a’

I am trying to define a parser in Haskell. I am a total beginner and somehow didn't manage to find any solution to my problem at all.
For the first steps I tried to follow the instructions on the slides of a powerpoint presentation. But I constantly get the error "Not in scope: type variable ‘a’":
type Parser b = a -> [(b,a)]
item :: Parser Char
item = \inp -> case inp of
[] -> []
(x:xs) -> [(x:xs)]
error: Not in scope: type variable ‘a’
|
11 | type Parser b = a -> [(b,a)]
| ^
I don't understand the error but moreover I don't understand the first line of the code as well:
type Parser b = a -> [(b,a)]
What is this supposed to do? On the slide it just tells me that in Haskell, Parsers can be defined as functions. But that doesn't look like a function definition to me. What is "type" doing here? If it s used to specify the type, why not use "::" like in second line above? And "Parser" seems to be a data type (because we can use it in the type definition of "item"). But that doesn't make sense either.
The line derives from:
type Parser = String -> (String, Tree)
The line I used in my code snippet above is supposed to be a generalization of that.
Your help would be much appreciated. And please bear in mind that I hardly know anything about Haskell, when you write an answer :D
There is a significant difference between the type alias type T = SomeType and the type annotation t :: SomeType.
type T = Int simply states that T is just another name for the type Int. From now on, every time we use T, it will be replaced with Int by the compiler.
By contrast, t :: Int indicates that t is some value of type Int. The exact value is to be specified by an equation like t = 42.
These two concepts are very different. On one hand we have equations like T = Int and t = 42, and we can replace either side with the other side, replacing type with types and values with values. On the other hand, the annotation t :: Int states that a value has a given type, not that the value and the type are the same thing (which is nonsensical: 42 and Int have a completely different nature, a value and a type).
type Parser = String -> (String, Tree)
This correctly defines a type alias. We can make it parametric by adding a parameter:
type Parser a = String -> (String, a)
In doing so, we can not use variables in the right hand side that are not parameters, for the same reason we can not allow code like
f x = x + y -- error: y is not in scope
Hence you need to use the above Parser type, or some variation like
type Parser a = String -> [(String, a)]
By contrast, writing
type Parser a = b -> [(b, a)] -- error
would use an undeclared type b, and is an error. At best, we could have
type Parser a b = b -> [(b, a)]
which compiles. I wonder, though, is you really need to make the String type even more general than it is.
So, going back to the previous case, a possible way to make your code run is:
type Parser a = String -> [(a, String)]
item :: Parser Char
item = \inp -> case inp of
[] -> []
(x:xs) -> [(x, xs)]
Note how [(x, xs)] is indeed of type [(Char, String)], as needed.
If you really want to generalize String as well, you need to write:
type Parser a b = b -> [(b, a)]
item :: Parser Char String
item = \inp -> case inp of
[] -> []
(x:xs) -> [(xs, x)]

Haskell - have a function call a function

we are currently sitting on a task from university, which we don't fully understand (please no solution but only ideas or suggestions).
What is given is a type:
type MyType = String -> String
Now we are trying to be able to have a function, which takes 2 Strings and a function (the type) and then gives a function (type)
myCode :: String -> String -> MyType -> MyType
and we already implemented a function, which can be used as MyType one:
emptyString :: MyType
emptyString :: (\a -> "")
The task is to be able to store several 2x Strings. This is our current idea:
myCode :: String -> String -> MyType ->MyType
myCode a b c = (\x -> b)
in this case we have an input String, which is "Hello" and another one which is "World" and then as c we put in the "emptyString".
This works for one String, because when we type the following in the console:
a = (myCode "Hello" "World" emptyString) ""
we get "World" on input "a". Now the hard part: We should be able to store several of these (searching them is another task, not needed right now). We thought we might be able to use "a" now when declaring another variable:
b = (myCode "1" "2" a) "Hello" "World" emptyString "")
This would call in "b" the function saved as "a" and within this the "emptyString".
As you may have guessed - it doesn't work! And we are really at a loss on how to carry on from now.
When you reached this part, it means you took the time to understand our complicated explanation of our task - thanks a lot.
Thanks for suggestions and help in advance!
From the question linked by amalloy in the comments, it looks like you are trying to build a phonebook based on a continuation passing style like paradigm.
Basically, what is supposed to happen for your type
myCode :: String -> String -> MyType -> MyType
is that you will generate a piece of data dat = myCode a b pb, which is of type MyType. So, you can query dat with an s :: String and it will output another String. In the operation of dat s, if you expand it to the definition,
dat s = myCode a b pb s
you have access to three strings, a, b, and whatever pb s returns. You will build up functionality recursively, either by doing something with a b and s, or pushing it down the road to pb, letting the continuation handle it.
Hope this helps without giving too much away.

F# assign (custom) type to a function

For all the progress I've made in F#, I still get lost in various of the constructor and deconstructor syntax.
I'm running a recursive simulation. One of the parameters is a function for the stopping condition. I have various possible stopping conditions to choose from. I make them all have the same signature. So I decide it would be nice, and educational, to lock down these functions to a custom type--so that not just any function that happens to match the signature can be sent:
type StoppingCondition = | StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
I think I'm doing this right, from tutorials, having a type name and an identical constructor name (confusing...), for a single case discriminated union. But now I can't figure out how to apply this type to an actual function:
let Condition1 lastRet nextRet i fl =
true
How do I make Condition1 be of type StoppingCondition? I bet it's trivial. But I've tried putting StoppingCondition as the first, second or last term after let, with and without parens and colons. And everything is an error.
Thanks for the patience found here.
EDIT:
I'll try to synthesize what I lean from the four answers (as of this moment), all good:
Trying to mimic this pattern:
s : string = "abc"
I was trying to write:
type StoppingCondition = | StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
let condition1 lastRet nextRet i fl : StoppingCondition = // BAD
//wrong for a type alias, totally wrong for a union constructor
true
//or
let condition1 : StoppingCondition lastRet nextRet i fl = // BAD again
true
or other insertions of : Stopping Condition (trying to prefix it, in the way that constructors go, in that one line).
Now I see that to get what I was getting at I would have to do:
type StoppingCondition = | StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
let conditionFunc1 lastRet nextRet i fl = //...
true
let stoppingCondition1 = StoppingCondition conditionFunc1
//or
let stoppingCondition2 = StoppingCondition <| (func lastRet nextRet i fl -> false)
//and there's another variation or 2 below
And what I didn't appreciate as a big negative to this approach is how a union type is different from a type alias. A type alias of string admits of the string functions when declared--it really is a string and does "string things. A single case discriminated union of string--is not a string any more. To have it do "string things" you have to unwrap it. (Or write versions of those functions into your type (which might be wrappers of the string functions).) Likewise a type alias of my function accepts those parameters. A DU of my function is just a wrapper and doesn't take arguments. So this doesn't work with discriminated union:
let x = stoppingCondition1 ret1 ret2 2 3.0 // BAD
//"stoppingCondition1 is not a function and cannot be applied"
And there's not enough value in my case here to work around the wrapper. But a type alias works:
type StoppingAlias = ReturnType -> ReturnType -> int -> float -> bool
let stoppingCondition:StoppingAlias = fun prevRet nextRet i x -> true
let b = stoppingCondition ret1 ret2 10 1.0 // b = true
I may not have everything straight in what I just said, but I think I'm a lot closer.
Edit 2:
Side note. My question is about defining the type of a function. And it compares using a type alias and a union type. As I worked at trying to do these, I also learned this about using a type alias:
This works (from: https://fsharpforfunandprofit.com/posts/defining-functions/ ):
type Adder = decimal -> decimal -> decimal
let f1 : Adder = (fun x y -> x + y)
//or
let f2 : decimal -> decimal -> decimal = fun x y -> x + y
but these are wrong:
let (f2 : Adder) x y = x + y // bad
let (f3 x y) : (decimal -> decimal -> decimal) = x + y // bad
let (f3 : (decimal -> decimal -> decimal)) x y = x + y // bad
And some discussion on this whole issue: F# Type declaration possible ala Haskell?
(And also, yeah, "assigning a type" isn't the right thing to say either.)
You don't "make it be of type" StoppingCondition. You declare a value of type StoppingCondition and pass Condition1 as the parameter of the DU case constructor:
let stop = StoppingCondition Condition1
That means, however, that every time you want to access the function contained in your single DU case, you have to pattern match over it in some way; that can become annoying.
You say you don't want just any functions that fulfill the signature to be valid as stopping conditions; however, it seems to be specific enough to avoid "accidentally" passing in an "inappropriate" function - with that, you could do something simpler - define StoppingCondition as a type alias for your specific function type:
type StoppingCondition = ReturnType -> ReturnType -> int -> float -> bool
Now you can use StoppingCondition everywhere you need to specify the type, and the actual values you pass/return can be any functions that fulfill the signature ReturnType -> ReturnType -> int -> float -> bool.
As has been said, you have to construct an instance of a StoppingCondition from an appropriate function, for example:
let Condition1 = StoppingCondition (fun _ _ _ _ -> true)`
One nice way to do this without weird indentation or extra parentheses is a backward pipe:
let Condition1 = StoppingCondition <| fun lastRet nextRet i fl ->
// Add function code here
The signature might be long enough to justify a record type instead of four curried parameters. It's a question of style and how it'll be used; the result may look like this:
type MyInput =
{ LastReturn : ReturnType
NextReturn : ReturnType
MyInt : int
MyFloat : float }
type StopCondition = StopCondition of (MyInput -> bool)
let impossibleCondition = StopCondition (fun _ -> false)
let moreComplicatedCondition = StopCondition <| fun inp ->
inp.MyInt < int (round inp.MyFloat)
To call the function inside a StopCondition, unwrap it with a pattern:
let testStopCondition (StopCondition c) input = c input
Specify the return type of a function is done like this:
let Condition1 lastRet nextRet i fl :StoppingCondition=
true
of course, this won't compile as true is not of the correct type.
I suspect the actual definition you want is closer to
let Condition1 :StoppingCondition=
true
though, as the type looks like it contains the function arguments.
Expanding on this, you can define such a function like:
let Condition1=fun a b c d -> StoppingCondition(fun a b c d -> whatever)
but this whole thing is pretty ugly.
Realistically, I think it is better to put all the functions in an array, which will force the types to match
So, it seems to me that you things you might want to with StoppingConditions are to create some predefined type of stopping condition.
Here are some examples of some possible stopping conditions:
let stopWhenNextGreaterThanLast = StoppingCondition (fun last next _ _ -> next > last)
let stopWhenLastGreaterThanLast = StoppingCondition (fun last next _ _ -> last> next)
(I've underscored the parameters I'm not using in my stopping condition definition)
Hopefully you can see that both of these values of type StoppingCondition.
Then you might want a function to determine if the stopping condition had been met given some parameters:
let shouldStop stoppingCond last next i value =
match stoppingCond with
|StoppingCondition f -> f last next i value
This function takes a stopping condition and the various states of your recursion and returns true or false depending on whether or not it should now stop.
This should be all you need to make use of this approach in practice.
You could extend this approach by doing something like this to cover multiple potential stopping conditions:
type StoppingCondition =
| StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
| Or of StoppingCondition * StoppingCondition
And modifying the shouldStop function
let rec shouldStop stoppingCond last next i value =
match stoppingCond with
|StoppingCondition f -> f last next i value
|Or (stp1, stp2) -> (shouldStop stp1 last next i value) || (shouldStop stp2 last next i value)
Now if we have a single condition, we stop when it's met or if we multiple conditions, we can check whether either of them are met.
Then you could Or together new stopping conditions from a base condition:
let stopWhenIIsEven = StoppingCondition (fun _ _ i _ -> i % 2 = 0)
let stopWhenValueIsZero = StoppingCondition (fun _ _ _ value -> value = 0.0)
let stopWhenIEvenOrValueZero = Or (stopWhenIIsEven, stopWhenValueIsZero)

How to read value of property depending on an argument

How can I get the value of a property given a string argument.
I have a Object CsvProvider.Row which has attributes a,b,c.
I want to get the attribute value depending on property given as a string argument.
I tried something like this:
let getValue (tuple, name: string) =
snd tuple |> Seq.averageBy (fun (y: CsvProvider<"s.csv">.Row) -> y.```name```)
but it gives me the following error:
Unexpected reserved keyword in lambda expression. Expected incomplete
structured construct at or before this point or other token.
Simple invocation of function should look like this:
getValue(tuple, "a")
and it should be equivalent to the following function:
let getValue (tuple) =
snd tuple |> Seq.averageBy (fun (y: CsvProvider<"s.csv">.Row) -> y.a)
Is something like this is even possible?
Thanks for any help!
The CSV type provider is great if you are accessing data by column names statically, because you get nice auto-completion with type inference and checking.
However, for a dynamic access, it might be easier to use the underlying CsvFile (also a part of F# Data) directly, rather than using the type provider:
// Read the given file
let file = CsvFile.Load("c:/test.csv")
// Look at the parsed headers and find the index of column "A"
let aIdx = file.Headers.Value |> Seq.findIndex (fun k -> k = "A")
// Iterate over rows and print A values
for r in file.Rows do
printfn "%A" (r.Item(aIdx))
The only unfortunate thing is that the items are accessed by index, so you need to build some lookup table if you want to easily access them by their name.

F# and statically checked union cases

Soon me and my brother-in-arms Joel will release version 0.9 of Wing Beats. It's an internal DSL written in F#. With it you can generate XHTML. One of the sources of inspiration have been the XHTML.M module of the Ocsigen framework. I'm not used to the OCaml syntax, but I do understand XHTML.M somehow statically check if attributes and children of an element are of valid types.
We have not been able to statically check the same thing in F#, and now I wonder if someone have any idea of how to do it?
My first naive approach was to represent each element type in XHTML as a union case. But unfortunately you cannot statically restrict which cases are valid as parameter values, as in XHTML.M.
Then I tried to use interfaces (each element type implements an interface for each valid parent) and type constraints, but I didn't manage to make it work without the use of explicit casting in a way that made the solution cumbersome to use. And it didn't feel like an elegant solution anyway.
Today I've been looking at Code Contracts, but it seems to be incompatible with F# Interactive. When I hit alt + enter it freezes.
Just to make my question clearer. Here is a super simple artificial example of the same problem:
type Letter =
| Vowel of string
| Consonant of string
let writeVowel =
function | Vowel str -> sprintf "%s is a vowel" str
I want writeVowel to only accept Vowels statically, and not as above, check it at runtime.
How can we accomplish this? Does anyone have any idea? There must be a clever way of doing it. If not with union cases, maybe with interfaces? I've struggled with this, but am trapped in the box and can't think outside of it.
It looks like that library uses O'Caml's polymorphic variants, which aren't available in F#. Unfortunately, I don't know of a faithful way to encode them in F#, either.
One possibility might be to use "phantom types", although I suspect that this could become unwieldy given the number of different categories of content you're dealing with. Here's how you could handle your vowel example:
module Letters = begin
(* type level versions of true and false *)
type ok = class end
type nok = class end
type letter<'isVowel,'isConsonant> = private Letter of char
let vowel v : letter<ok,nok> = Letter v
let consonant c : letter<nok,ok> = Letter c
let y : letter<ok,ok> = Letter 'y'
let writeVowel (Letter(l):letter<ok,_>) = sprintf "%c is a vowel" l
let writeConsonant (Letter(l):letter<_,ok>) = sprintf "%c is a consonant" l
end
open Letters
let a = vowel 'a'
let b = consonant 'b'
let y = y
writeVowel a
//writeVowel b
writeVowel y
Strictly speaking, if you want to distinguish between something at compile-time, you need to give it different types. In your example, you could define two types of letters and then the type Letter would be either the first one or the second one.
This is a bit cumbersome, but it's probably the only direct way to achieve what you want:
type Vowel = Vowel of string
type Consonant = Consonant of string
type Letter = Choice<Vowel, Consonant>
let writeVowel (Vowel str) = sprintf "%s is a vowel" str
writeVowel (Vowel "a") // ok
writeVowel (Consonant "a") // doesn't compile
let writeLetter = function
| Choice1Of2(Vowel str) -> sprintf "%s is a vowel" str
| Choice2Of2(Consonant str) -> sprintf "%s is a consonant" str
The Choice type is a simple discriminated union which can store either a value of the first type or a value of the second type - you could define your own discriminated union, but it is a bit difficult to come up with reasonable names for the union cases (due to the nesting).
Code Contracts allow you to specify properties based on values, which would be more appropriate in this case. I think they should work with F# (when creating F# application), but I don't have any experience with integrating them with F#.
For numeric types, you can also use units of measure, which allow you to add additional information to the type (e.g. that a number has a type float<kilometer>), but this isn't available for string. If it was, you could define units of measure vowel and consonant and write string<vowel> and string<consonant>, but units of measure focus mainly on numerical applications.
So, perhaps the best option is to rely on runtime-checks in some cases.
[EDIT] To add some details regarding the OCaml implementation - I think that the trick that makes this possible in OCaml is that it uses structural subtyping, which means (translated to the F# terms) that you can define discriminated union with some mebers (e.g. only Vowel) and then another with more members (Vowel and Consonant).
When you create a value Vowel "a", it can be used as an argument to functions taking either of the types, but a value Consonant "a" can be used only with functions taking the second type.
This unfrotunately cannot be easily added to F#, because .NET doesn't natively support structural subtyping (although it may be possible using some tricks in .NET 4.0, but that would have to be done by the compiler). So, I know understand your problem, but I don't have any good idea how to solve it.
Some form of structural subtyping can be done using static member constraints in F#, but since discriminated union cases aren't types from the F# point of view, I don't think it is usable here.
My humble suggestion is: if the type system does not easily support statically checking 'X', then don't go through ridiculous contortions trying to statically check 'X'. Just throw an exception at runtime. The sky will not fall, the world will not end.
Ridiculous contortions to gain static checking often come at the expense of complicating an API, and make error messages indecipherable, and cause other degradations at the seams.
You can use inline functions with statically-resolved type parameters to yield different types depending on context.
let inline pcdata (pcdata : string) : ^U = (^U : (static member MakePCData : string -> ^U) pcdata)
let inline a (content : ^T) : ^U = (^U : (static member MakeA : ^T -> ^U) content)
let inline br () : ^U = (^U : (static member MakeBr : unit -> ^U) ())
let inline img () : ^U = (^U : (static member MakeImg : unit -> ^U) ())
let inline span (content : ^T) : ^U = (^U : (static member MakeSpan : ^T -> ^U) content)
Take the br function, for example. It will produce a value of type ^U, which is statically resolved at compilation. This will only compile if ^U has a static member MakeBr. Given the example below, that could produce either a A_Content.Br or a Span_Content.Br.
You then define a set of types to represent legal content. Each exposes "Make" members for the content that it accepts.
type A_Content =
| PCData of string
| Br
| Span of Span_Content list
static member inline MakePCData (pcdata : string) = PCData pcdata
static member inline MakeA (pcdata : string) = PCData pcdata
static member inline MakeBr () = Br
static member inline MakeSpan (pcdata : string) = Span [Span_Content.PCData pcdata]
static member inline MakeSpan content = Span content
and Span_Content =
| PCData of string
| A of A_Content list
| Br
| Img
| Span of Span_Content list
with
static member inline MakePCData (pcdata : string) = PCData pcdata
static member inline MakeA (pcdata : string) = A_Content.PCData pcdata
static member inline MakeA content = A content
static member inline MakeBr () = Br
static member inline MakeImg () = Img
static member inline MakeSpan (pcdata : string) = Span [PCData pcdata]
static member inline MakeSpan content = Span content
and Span =
| Span of Span_Content list
static member inline MakeSpan (pcdata : string) = Span [Span_Content.PCData pcdata]
static member inline MakeSpan content = Span content
You can then create values...
let _ =
test ( span "hello" )
test ( span [pcdata "hello"] )
test (
span [
br ();
span [
br ();
a [span "Click me"];
pcdata "huh?";
img () ] ] )
The test function used there prints XML... This code shows that the values are reasonable to work with.
let rec stringOfAContent (aContent : A_Content) =
match aContent with
| A_Content.PCData pcdata -> pcdata
| A_Content.Br -> "<br />"
| A_Content.Span spanContent -> stringOfSpan (Span.Span spanContent)
and stringOfSpanContent (spanContent : Span_Content) =
match spanContent with
| Span_Content.PCData pcdata -> pcdata
| Span_Content.A aContent ->
let content = String.concat "" (List.map stringOfAContent aContent)
sprintf "<a>%s</a>" content
| Span_Content.Br -> "<br />"
| Span_Content.Img -> "<img />"
| Span_Content.Span spanContent -> stringOfSpan (Span.Span spanContent)
and stringOfSpan (span : Span) =
match span with
| Span.Span spanContent ->
let content = String.concat "" (List.map stringOfSpanContent spanContent)
sprintf "<span>%s</span>" content
let test span = printfn "span: %s\n" (stringOfSpan span)
Here's the output:
span: <span>hello</span>
span: <span><br /><span><br /><a><span>Click me</span></a>huh?<img /></span></span>
Error messages seem reasonable...
test ( div "hello" )
Error: The type 'Span' does not support any operators named 'MakeDiv'
Because the Make functions and the other functions are inline, the generated IL is probably similar to what you would code by hand if you were implementing this without the added type safety.
You could use the same approach to handle attributes.
I do wonder if it will degrade at the seams, as Brian pointed out contortionist solutions might. (Does this count as contortionist or not?) Or if it will melt the compiler or the developer down by the time it implements all of XHTML.
Classes?
type Letter (c) =
member this.Character = c
override this.ToString () = sprintf "letter '%c'" c
type Vowel (c) = inherit Letter (c)
type Consonant (c) = inherit Letter (c)
let printLetter (letter : Letter) =
printfn "The letter is %c" letter.Character
let printVowel (vowel : Vowel) =
printfn "The vowel is %c" vowel.Character
let _ =
let a = Vowel('a')
let b = Consonant('b')
let x = Letter('x')
printLetter a
printLetter b
printLetter x
printVowel a
// printVowel b // Won't compile
let l : Letter list = [a; b; x]
printfn "The list is %A" l
Thanks for all the suggestions! Just in case it will inspire anyone to come up with a solution to the problem: below is a simple HTML page written in our DSL Wing Beats. The span is a child of the body. This is not valid HTML. It would be nice if it didn't compile.
let page =
e.Html [
e.Head [ e.Title & "A little page" ]
e.Body [
e.Span & "I'm not allowed here! Because I'm not a block element."
]
]
Or are there other ways to check it, that we have not thought about? We're pragmatic! Every possible way is worth investigating. One of the major goals with Wing Beats is to make it act like an (X)Html expert system, that guides the programmer. We want to be sure a programmer only produces invalid (X)Html if he chooses to, not because of lacking knowledge or careless mistakes.
We think we have a solution for statically checking the attributes. It looks like this:
module a =
type ImgAttributes = { Src : string; Alt : string; (* and so forth *) }
let Img = { Src = ""; Alt = ""; (* and so forth *) }
let link = e.Img { a.Img with Src = "image.jpg"; Alt = "An example image" };
It has its pros and cons, but it should work.
Well, if anyone comes up with anything, let us know!