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.
Related
I need to call bool.TryParse in F# and I'm having a little difficulty with the compiler / Ionide.
Ex.
let value = true
let parsable = bool.TryParse("True", &value)
There error I'm seeing in Ionide for VSCode looks like the following.
val value : bool Full name:
value
Assembly: example3
The type 'ByRefKinds.InOut' does not match the type 'ByRefKinds.In'F#
Compiler(1)
This is the first example that I've used call by ref keywords and syntax in F# so I'm not sure what I'm doing wrong and the byrefs documentation doesn't seem to be of much help on understanding this particular scenario.
It turns out that the main thing I was missing in my example was that value wasn't mutable as it would need to be when called in this context.
Working example:
let mutable value = true
let parsable = bool.TryParse("True", &value)
This solution came to me after reading a slightly related GitHub issue related to Span<T> and then also playing around in dotnet fsi which at least gave me the following clue.
let parsable = bool.TryParse("True", &value);;
-------------------------------------^^^^^^
stdin(34,38): error FS3230: A value defined in a module must be mutable in order to take its address, e.g. 'let mutable x = ...'
As it turns out however F# also seems to have sntaxtic sugar around the Try-Parse pattern which I began to recall after playing around with this example further. This reduced into the below alternative solution to calling bool.TryParse.
let value = true
let (parsable, _) = bool.TryParse "True"
Alternatively without having to bind parsable and to specify the default value within a single expression the following example may be more elegant.
let value = match bool.TryParse "True" with
| true, value -> value
| false, _ -> true
Perhaps there are pros and cons to the different ways of calling bool.TryParse and other Try-Parse methods but the important thing is that I found some solutions that work and get me past the initial stumbling blocks with regard to lack of F# documentation on the subject.
As you have already discovered, the error can be fixed by making the 'value' a mutable variable.
Saying that, the last solution solution you shared (using pattern matching) looks the nicest and safest.
If you happen to do many of these parsing operations, I would suggest extracting some utility functions. That will avoid having to correctly pattern match on (true, value) rather than (false, value) by mistake.
Sample code:
open System
module Option =
let ofTry<'a> (res:(bool*'a)) : 'a option =
match res with
| true, res -> Some res
| false, _ -> None
module Result =
let ofTry<'a,'b> (err:'b) (res:(bool*'a)) : Result<'a, 'b> =
res
|> csharpToOption
|> Option.toResult err
module Samples =
let withPatternMatch =
match Int32.TryParse "5" with
| true, value -> value |> Some
| false, _ -> None
let withOptionHelper = Int32.TryParse "5" |> Option.ofTry
let withResultHelper = Int32.TryParse "5" |> Result.ofTry "Not a number"
I'd like to tidy my Eralng code, I found there're lots of issue following:
A = {Tid, _Tv0, _Tv1, Tv2, Tv3}
Is there any way to clean the code like to be: A = {Tid, SomewayReplace(4)} ???
Update1:
like #Pascal example, Is there any way to simple the code A = {T, _, _, _, _, _} like to be A = {T, SomewayReplace(4)} to replace that 4 symbol _ ???
update2
in real project, if some record include many element, I found it force me to repeat writing the symbol _, so I wonder if there is any way to simple it???
Writting A = Something means that you try to match A with Something or if A is unbound, assign Something to A. In anycase, Something must be defined.
You can find some shortcut in writting. For example, if you want to assign the result of a funtion to A, verify that the result is a tuple of 5 elements and assign the first element to T, the you can write:
A = {T,_,_,_,_} = f(Param).
The meaning of _T is exactly the same as any variable. It just says to th compiler to not issue a warning if this variable is not used in the code. It is frequent in pattern matching when you want to ignore the value of a variable but still keep trace of its meaning.
[edit]
It is not possible to write {T, SomewayReplace(4)}, but you may use records. A record is a tagged tuple (first element is the atom that identify this record. It is not shorter than placeholder for small tuples, but it is clearer, you don't need to remember the location of the information in your tuple, and it is easier to modify your code when you need to add a new element in a tuple. The syntax will be
-record(mytuple,{field1,...,fieldx,...}.
...
A = #mytuple{fieldx = T} = f(Param).
waerning: Records are managed by the compiler, so everything must be known at build time (#mytuple{Fieldx = T} is illegal, Fieldx cannot be a variable).
Why not use a record? Then you only match the fields you want to extract. As a by-effect, you make the code easier to debug, since you are forced to name the tuple by having a atom first.
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.
Extremely just-started-yesterday new to F#.
What I want: To write code that parses the string "2 + 2" into (using as an example code from the tutorial project) Expr.Add(Expr.Num 2, Expr.Num 2) for evaluation. Some help to at least point me in the right direction or tell me it's too complex for my first F# project. (This is how I learn things: By bashing my head against stuff that's hard)
What I have: My best guess at code to extract the numbers. Probably horribly off base. Also, a lack of clue.
let script = "2 + 2";
let rec scriptParse xs =
match xs with
| [] -> (double)0
| y::ys -> (double)y
let split = (script.Split([|' '|]))
let f x = (split[x]) // "This code is not a function and cannot be applied."
let list = [ for x in 0..script.Length -> f x ]
let result = scriptParse
Thanks.
The immediate issue that you're running into is that split is an array of strings. To access an element of this array, the syntax is split.[x], not split[x] (which would apply split to the singleton list [x], assuming it were a function).
Here are a few other issues:
Your definition of list is probably wrong: x ranges up to the length of script, not the length of the array split. If you want to convert an array or other sequence to a list you can just use List.ofSeq or Seq.toList instead of an explicit list comprehension [...].
Your "casts" to double are a bit odd - that's not the right syntax for performing conversions in F#, although it will work in this case. double is a function, so the parentheses are unnecessary and what you are doing is really calling double 0 and double y. You should just use 0.0 for the first case, and in the second case, it's unclear what you are converting from.
In general, it would probably be better to do a bit more design up front to decide what your overall strategy will be, since it's not clear to me that you'll be able to piece together a working parser based on your current approach. There are several well known techniques for writing a parser - are you trying to use a particular approach?
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.)