Is there another way to get the value printed by the %A specifier (for DUs particularly)? printf is too slow for my current use.
The answer is apparently no. Overriding ToString seems to be the preferred alternative.
Related
I am learning F# but I just don't understand how I am supposed to use ToString. Below are a few attempts. The syntax errors are saying it is expecting type string but that it is actually type uint -> string. So it doens't actually appear to be invoking a function? Could this be explained? This seems like such a simple thing to do but I can't figure it out.
open System
open System.IO
open FSharp.Data
[<EntryPoint>]
let main (args: string[]) =
let htmlPage = HtmlDocument.Load("https://scrapethissite.com/")
printfn "%s" htmlPage.ToString // This causes a syntax error
htmlPage.ToString
|> (fun x -> printfn "%s" x) // This also causes a syntax error
0
.ToString is a method, not a value. In F# every method and every function has a parameter. In fact, that's how functions differ from values (and methods from properties): by having a parameter.
Unlike in C#, F# methods and functions cannot be parameterless. If there is nothing meaningful that you'd want to pass to the method, that method would still have one parameter of type unit. See how this is visible in the error message? unit -> string is the type.
To call such method, you have to pass it the parameter. The sole value of type unit is denoted (). So to call the method you should do:
htmlPage.ToString ()
|> printfn "%s"
Your first example is a bit more complicated. The following would not work:
printfn "%s" htmlPage.ToString ()
Why? Because according to F# syntax this looks like calling printfn and passing it three parameters: first "%s", then htmlPage.ToString, and finally (). To get the correct order of calls you have to use parentheses:
printfn "%s" (htmlPage.ToString ())
And finally, general piece of advice: when possible try to avoid methods and classes in F# code. Most things can be done with functions. In this particular case, the ToString methods can be replaced with the equivalent function string:
printfn "%s" (string htmlPage)
As far as I'm aware, F# doesn't handle printfn like it does other functions because of it's type dependence on the string parameter. Could someone explain to me why that prevents me from doing
let printn = printfn "%A"
The error says the type is infered to ('a -> unit) and that I need to add an explicit parameter or a type annotation. Why? I'm perfectly fine with the type it inferred it to. Also, if I add an explicit point, it still has the same type.
Because printn would be a simple value the way you define it. And values can't be generic in the .NET runtime.
Make the parameter explicit so it is a function:
let printn value = printfn "%A" value
For more information, see the topic Value Restriction on MSDN.
By running printfn "%A" "c", I get "c".
By running printfn "%s" "c", I get c.
Why the difference? The same goes for char.
The %A specifier tries to hint at object types - the "c" is it trying to show it is a string. When you do %s the compiler knows you want to print a string so it doesn't print the quotes
Because printfn "%A" uses reflection, it displays results the same as values automatically printed out by F# Interactive. On the other hand, %s is for strings only, and it shows contents of strings.
The generic case of "%s" is "%O" when ToString methods are used. The %A specifier is slow, but helpful for structural types and types without overridden ToString methods.
I am writing a F# program which parses a string into a AST type which is a discriminated union.
When I use fsi (on Mono + Mac OS X) to run my code, the AST is printed out in a nice format. But when I use printfn "%s" <| ast.ToString() I get something like FSI_0002.Absyn+clazz. Writing a ToString method for all the discriminated union types would be a big chore.
How do I make the value print the way fsi does it?
Have you tried printfn "%A" ast? The %A specifier takes into consideration the StructuredFormatDisplayAttribute[MSDN], if present.
To convert a discriminated union into a string, you should use sprintf "%A" ast instead of ast.ToString().
If you want Enum.GetName, you can use Microsoft.FSharp.Reflection namespace. See What is the Enum.GetName equivalent for F# union member?.
In addition to Daniel's comment, here is a good blog article explaining how to format it in whatever way you'd wish:
http://blogs.msdn.com/b/dsyme/archive/2010/01/08/some-tips-and-tricks-for-formatting-data-in-f-interactive-and-a-in-sprintf-printf-fprintf.aspx (web.archive.org)
I've just found something I'd call a quirk in F# and would like to know whether it's by design or by mistake and if it's by design, why is it so...
If you write any range expression where the first term is greater than the second term the returned sequence is empty. A look at reflector suggests this is by design, but I can't really find a reason why it would have to be so.
An example to reproduce it is:
[1..10] |> List.length
[10..1] |> List.length
The first will print out 10 while the second will print out 0.
Tests were made in F# CTP 1.9.6.2.
EDIT: thanks for suggesting expliciting the range, but there's still one case (which is what inspired me to ask this question) that won't be covered. What if A and B are variables and none is constantly greater than the other although they're always different?
Considering that the range expression does not seem to get optimized at compiled time anyway, is there any good reason for the code which determines the step (not explicitly specified) in case A and B are ints not to allow negative steps?
As suggested by other answers, you can do
[10 .. -1 .. 1] |> List.iter (printfn "%A")
e.g.
[start .. step .. stop]
Adam Wright - But you should be able
to change the binding for types you're
interested in to behave in any way you
like (including counting down if x >
y).
Taking Adam's suggestion into code:
let (..) a b =
if a < b then seq { a .. b }
else seq { a .. -1 .. b }
printfn "%A" (seq { 1 .. 10 })
printfn "%A" (seq { 10 .. 1 })
This works for int ranges. Have a look at the source code for (..): you may be able to use that to work over other types of ranges, but not sure how you would get the right value of -1 for your specific type.
What "should" happen is, of course, subjective. Normal range notation in my mind defines [x..y] as the set of all elements greater than or equal to x AND less than or equal to y; an empty set if y < x. In this case, we need to appeal to the F# spec.
Range expressions expr1 .. expr2 are evaluated as a call to the overloaded operator (..), whose default binding is defined in Microsoft.FSharp.Core.Operators. This generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr2) values, using an increment of 1. The operator requires the existence of a static member (..) (long name GetRange) on the static type of expr1 with an appropriate signature.
Range expressions expr1 .. expr2 .. expr3 are evaluated as a call to the overloaded operator (.. ..), whose default binding is defined in Microsoft.FSharp.Core.Operators. This generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr3) values, using an increment of expr2. The operator requires the existence of a static member (..) (long name GetRange) on the static type of expr1 with an appropriate signature.
The standard doesn't seem to define the .. operator (a least, that I can find). But you should be able to change the binding for types you're interested in to behave in any way you like (including counting down if x > y).
In haskell, you can write [10, 9 .. 1]. Perhaps it works the same in F# (I haven't tried it)?
edit:
It seems that the F# syntax is different, maybe something like [10..-1..1]
Ranges are generally expressed (in the languages and frameworks that support them) like this:
low_value <to> high_value
Can you give a good argument why a range ought to be able to be expressed differently? Since you were requesting a range from a higher number to a lower number does it not stand to reason that the resulting range would have no members?