F# odd pattern matching issues - f#

While writing some code yesterday, I ran into two odd problems, which neither me nor my functional programming oriented friend could figure out. We have looked at it for quite some time, and researched it on the net, but we were not able to find any answers anywhere, so here goes:
The issue is that in this code:
First weird problem:
let outer1 (bs : byte array) =
let rec inner (bs : byte array) (bacc : byte array) (i : int) =
match i with
| bs.Length -> bacc // <--- Error: bs is not recognized. Why?
| _ -> bacc.[i] <- bs.[i]
inner bs bacc (i + 1)
inner bs (Array.zeroCreate bs.Length) 0
The problem here is: FS0039: The namespace or module 'bs' is not defined.
How can this be? bs is in the function signature after all. Moreover, defining a new value with let bsLength = bs.Length works right before the match. But by doing so I see a new oddity:
let outer2 (bs : byte array) =
let rec inner (bs : byte array) (bacc : byte array) (i : int) =
let bsLength = bs.Length
match i with
| bsLength -> bacc
| _ -> bacc.[i] <- bs.[i] // <--- Warning: Rule never matched. Why?
inner bs bacc (i + 1)
inner bs (Array.zeroCreate bs.Length) 0
Here the problem is a warning that says: warning FS0026: This rule will never be matched.
I don't get that at all. i and the length of the array has no relation to each other. If I write an integer (for instance 10) instead of bsLength, the warning disappears.

Both your problems stem from the expectation that pattern matching allows using values and literals interchangeably. No, it does not. Pattern Matching (F#) topic on MSDN gives a good overview of supported pattern types and precedence rules of their application. The major principle simplifying a lengthy description there is: you cannot match a value unless this value is a literal, or identifier (a case value of a discriminated union, an exception label, or an active pattern case).
In your first problem point compiler treats bs.Length not as a property Length of array bs as you expect, but as a literal or identifier Length from non-existing module or namespace bs; as John Palmer pointed in his answer you may achieve the expected behavior by using variable pattern with a guard statement. A sample of legitimate use of the pattern matching expression resembling yours would be:
module bs =
[<Literal>]
let Length = 100
//.............................
let v = 100;
let s = match v with
| bs.Length -> "matched"
| _ -> "not matched";;
val s : string = "matched"
The second problem point is treated by compiler as variable pattern, and bsLength is assigned a value of i instead of values being compared, as you expected; second matching rule does not have chances to kick in.

The match statement doesn't work like you think it does - the correct syntax is
match i with
| t when t = bs.Length
In the second case, you actually create a new variable called bsLength which hides the definition of the earlier bsLength and matches all integers, so you get the rule never matched warning.

Related

Can I Use Multiple Properties in a StructuredFormatDisplayAttribute?

I'm playing around with StructuredFormatDisplay and I assumed I could use multiple properties for the Value, but it seems that is not the case. This question (and accepted answer) talk about customizing in general, but the examples given only use a single property. MSDN is not helpful when it comes to usage of this attribute.
Here's my example:
[<StructuredFormatDisplay("My name is {First} {Last}")>]
type Person = {First:string; Last:string}
If I then try this:
let johnDoe = {First="John"; Last="Doe"}
I end up with this error:
<StructuredFormatDisplay exception: Method 'FSI_0038+Person.First}
{Last' not found.>
The error seems to hint at it only capturing the first property mentioned in my Value but it's hard for me to say that with any confidence.
I have figured out I can work around this by declaring my type like this:
[<StructuredFormatDisplay("My name is {Combined}")>]
type Person = {First:string; Last:string} with
member this.Combined = this.First + " " + this.Last
But I was wondering if anyone could explain why I can't use more than one property, or if you can, what syntax I'm missing.
I did some digging in the source and found this comment:
In this version of F# the only valid values are of the form PreText
{PropertyName} PostText
But I can't find where that limitation is actually implemented, so perhaps someone more familiar with the code base could simply point me to where this limitation is implemented and I'd admit defeat.
The relevant code from the F# repository is in the file sformat.fs, around line 868. Omitting lots of details and some error handling, it looks something like this:
let p1 = txt.IndexOf ("{", StringComparison.Ordinal)
let p2 = txt.LastIndexOf ("}", StringComparison.Ordinal)
if p1 < 0 || p2 < 0 || p1+1 >= p2 then
None
else
let preText = if p1 <= 0 then "" else txt.[0..p1-1]
let postText = if p2+1 >= txt.Length then "" else txt.[p2+1..]
let prop = txt.[p1+1..p2-1]
match catchExn (fun () -> getProperty x prop) with
| Choice2Of2 e ->
Some (wordL ("<StructuredFormatDisplay exception: " + e.Message + ">"))
| Choice1Of2 alternativeObj ->
let alternativeObjL =
match alternativeObj with
| :? string as s -> sepL s
| _ -> sameObjL (depthLim-1) Precedence.BracketIfTuple alternativeObj
countNodes 0 // 0 means we do not count the preText and postText
Some (leftL preText ^^ alternativeObjL ^^ rightL postText)
So, you can easily see that this looks for the first { and the last }, and then picks the text between them. So for foo {A} {B} bar, it extracts the text A} {B.
This does sound like a silly limitation and also one that would not be that hard to improve. So, feel free to open an issue on the F# GitHub page and consider sending a pull request!
Just to put a bow on this, I did submit a PR to add this capability and yesterday it was accepted and pulled into the 4.0 branch.
So starting with F# 4.0, you'll be able to use multiple properties in a StructuredFormatDisplay attribute, with the only downside that all curly braces you wish to use in the message will now need to be escaped by a leading \ (e.g. "I love \{ braces").
I rewrote the offending method to support recursion and switched to using a regular expression to detect property references. It seems to work pretty well, although it isn't the prettiest code I've ever written.

How do I check whether a variable is an integer in F#?

I'm new in F#.
How do I check whether a variable is an integer or another type.
Thanks.
One way is listed by #ildjarn in the comments:
let isInt x = box x :? int
An idiomatic way would be to use pattern matching. First, define a discriminated union which defines the possible options:
type Test =
| IsAnInteger of int
| IsADouble of double
| NotANumber of Object
then use a match statement to determine which option you got. Note that when you initially create the value you wish to use with a match statement, you need to put it into the discriminated union type.
let GetValue x =
match x with
| IsAnInteger(a) -> a
| IsADouble(b) -> (int)b
| NotAnInteger(_) -> 0
Since you're probably going to use your test to determine control flow, you might as well do it idiomatically. This can also prevent you from missing cases since match statements give you warnings if you don't handle all possible cases.
>GetValue (NotAnInteger("test"));;
val it : int = 0
>GetValue (IsADouble(3.3))
val it : int = 3
>GetValue (IsAnInteger(5))
val it : int = 5
Considering that you tagged this question "c#-to-f#" I'm assuming you're coming to F# from a C# background. Therefore I think you may be a bit confused by the type inference since you're probably used to explicitly typing variables.
You can explicitly declare the type of a value if you need to.
let x:int = 3
But it's usually easier and better to let the type inference do this work for you. You'll note that I said value--the declaration above is not a variable because you cannot do a destructive assignment to it. If you want a variable then do this:
let mutable x:int = 3
You can then assign a new value to x via this construct
x <- 5
But, as a rule, you'll want to avoid mutable values.

Transform an Abstract Syntax Tree (AST) in F#

I am trying to design an AST for a decision logic table. One of the things I would like to be able to do with the discriminated union that represents my AST is transform parts of it for different reasons. For clarity I will give you an example
Decision Logic Table
# VAR = 10 ;Y;
The above can be read as there is one rule and the condition VAR = 10 enters this rule with a Y entry.
Abstract Syntax Tree Definition (simplified for this example)
type expression =
| Value of double
| Variable of string
| Equality of expression * expression
type entry =
| Entry of string
type entries =
| Entries of entry list
type conditional =
| ConditionEntries of expression * entries
type condition
| Condition of expression * string
type rule =
| Rule of condition list
Rendered (before transform)
ConditionEntries(
Equality(
Variable("VAR"),
Value(10.0)),
Entries(["Y"]))
Rendered (after transform)
Rule(
Condition(
Equality(
Variable("VAR"),
Value(10.0)
),
Entry("Y")
)
)
Now what I would like to do is transform the above tree to expand the rules that are represented in the entries. My thinking was I could use a recursive function and pattern-matching to do this but I am having a little trouble wrapping my head around it right now.
I guess in essence what I am trying to do is whenever I see a ConditionEntries node, I want to emit a new Rule for every string in the Entries list where the Condition is combined with the Entry. Does that make any sense?
Thanks in advance for any advice.
p.s. I haven't quite tried to compile the above example, so please forgive any grammatical errors.
Hmm, based on your AST, which is awfully broken up, here is a tranform function which produces the output from input you desire (though it's not recursive, just uses List.map with some pattern matching. expression is your only recursive type but it doesn't look like you want to process it recursively?):
let ex1 =
ConditionEntries(
Equality(
Variable("VAR"),
Value(10.0)),
Entries([Entry("Y")]))
let ex2 =
ConditionEntries(
Equality(
Variable("VAR"),
Value(10.0)),
Entries([Entry("X");Entry("Y");Entry("Z")]))
let transform ces =
match ces with
| ConditionEntries(x, Entries(entries)) ->
entries
|> List.map (function Entry(entry) -> Condition(x, entry))
//FSI output:
> transform ex1;;
val it : condition list =
[Condition (Equality (Variable "VAR",Value 10.0),"Y")]
> transform ex2;;
val it : condition list =
[Condition (Equality (Variable "VAR",Value 10.0),"X");
Condition (Equality (Variable "VAR",Value 10.0),"Y");
Condition (Equality (Variable "VAR",Value 10.0),"Z")]

How to extract data from F# list

Following up my previous question, I'm slowly getting the hang of FParsec (though I do find it particularly hard to grok).
My next newbie F# question is, how do I extract data from the list the parser creates?
For example, I loaded the sample code from the previous question into a module called Parser.fs, and added a very simple unit test in a separate module (with the appropriate references). I'm using XUnit:
open Xunit
[<Fact>]
let Parse_1_ShouldReturnListContaining1 () =
let interim = Parser.parse("1")
Assert.False(List.isEmpty(interim))
let head = interim.Head // I realise that I have only one item in the list this time
Assert.Equal("1", ???)
Interactively, when I execute parse "1" the response is:
val it : Element list = [Number "1"]
and by tweaking the list of valid operators, I can run parse "1+1" to get:
val it : Element list = [Number "1"; Operator "+"; Number "1"]
What do I need to put in place of my ??? in the snippet above? And how do I check that it is a Number, rather than an Operator, etc.?
F# types (including lists) implement structural equality. This means that if you compare two lists that contain some F# types using =, it will return true when the types have the same length and contain elements with the same properties.
Assuming that the Element type is a discriminated union defined in F# (and is not an object type), you should be able to write just:
Assert.Equal(interim, [Number "1"; Operator "+"; Number "1"])
If you wanted to implement the equality yourself, then you could use pattern matching;
let expected = [Number "1"]
match interim, expected with
| Number a, Number b when a = b -> true
| _ -> false

F# - weird behavior when pattern matching discriminated unions

I've been writing some F# now for about 6 months and I've come across some behavior that I can't explain. I have some boiled down code below. (value names have been changed to protect the innocent!)
I have a hierarchy defined using record types rec1 and rec2, and also a dicriminated union type with possible values CaseA and CaseB. I'm calling a function ('mynewfunc') that takes a du_rec option type. Internally this function defines a recursive function that processes the hierarchy .
I'm kicking off the processing by passing the None option value to represent the root of the hierarchy (In reality, this function is deserializing the hierarchy from a file).
When I run the code below I hit the "failwith "invalid parent"" line of code. I can not understand why this is, because the None value that is passed down should match the outer pattern matching's None case.
The code works if I delete either of the sets of comments. This is not a showstopper for me - I just feel a bit uncomfortable not knowing why this is happening (I thought I was understanding f#)
Thanks in advance for any replies
James
type rec2 =
{
name : string
child : rec1 option
}
and rec1 =
{
name : string ;
child : rec2 option
}
and du_rec =
| Case1 of rec1
| Case2 of rec2
let mynewfunc (arg:du_rec option) =
let rec funca (parent:du_rec option) =
match parent with
| Some(node) ->
match node with
| Case2(nd) ->
printfn "hello"
(* | Case1(nd) ->
printfn "bye bye" *)
| _ ->
failwith "invalid parent"
| None ->
// printfn "case3"
()
funcb( None )
and funcb (parent: du_rec option) =
printfn "this made no difference"
let node = funca(arg)
()
let rootAnnot = mynewfunc(None)
Based on the comments, this is just a bad experience in the debugger (where the highlighting suggests that the control flow is going places it is not); the code does what you expect.
(There are a number of places where the F# compiler could improve its sequence-points generated into the pdbs, to improve the debugging experience; I think we'll be looking at this in a future release.)

Resources