Context:
Running F# in a containerized environment with dotnet 2.2.203
on Ubuntu 18.04 desktop machine
Question: The StructuredFormatDisplay in a Composed Record doesn't work.
Was it wrong?
These is the code
[<StructuredFormatDisplay("{SizeGb}GB")>]
type Disk =
{ SizeGb : int }
override __.ToString() = sprintf "<%dGB>" __.SizeGb
[<StructuredFormatDisplay("Computer #{Id}: {Manufacturer}/{DiskCount}:{Disks}")>]
type Computer =
{ Id: int
mutable Manufacturer: string
mutable Disks: Disk list }
override __.ToString() = sprintf "#%d<%s>%O" __.Id __.Manufacturer __.Disks
[<EntryPoint>]
let main argv =
let myPc =
{ Id = 0
Manufacturer = "Computers Inc."
Disks =
[ { SizeGb = 100 }
{ SizeGb = 250 }
{ SizeGb = 500 } ] }
printfn "%%O = %O" myPc
printfn "%%A = %A" myPc
0
And the output
%O = #0<Computers Inc.>[<100GB>; <250GB>; <500GB>]
%A = Computer #0: Computers Inc./3:[...GB; ...GB; ...GB]
The %A pattern for the Disk record in Computer record just print some ... dots!
But %O is well printed.
I confirm that this issue also happens in my context too.
When you print %A directly on a disk list, output is okay:
printfn "%A" [{SizeGb = 10}] // output: [10GB]
But when the disk list is printed indirectly as in your code:
[<StructuredFormatDisplay("Computer #{Id}: {Manufacturer}/{DiskCount}:{Disks}")>]
We receive dots.
I think this is a bug of the F# core library. One workaround could be adding a new string property holding the formatted string of the disk list, and use that property instead:
[<StructuredFormatDisplay("Computer #{Id}: {Manufacturer}/{DiskCount}:{DisksStr}")>]
type Computer =
{ Id: int
mutable Manufacturer: string
mutable Disks: Disk list }
member this.DisksStr = sprintf "%A" this.Disks
Related
I have this code and the intent is to only print cases of the Union Type Event:
type CustomerPromoted = { id: string; level: int }
type CustomerCreated = { id: string; companyName: string }
type Event =
| Created of CustomerCreated
| Promoted of CustomerPromoted
let printEvents (events: Event list) =
events
|> List.iter (fun event -> printfn "%A" event)
[<EntryPoint>]
let main argv =
let created = { id = "1"; companyName = "MS" }
let promoted = { id = "1"; level = 3 }
printEvents [ created, promoted ]
0
But the line
printEvents [ created, promoted ]
Results in this error:
This expression was expected to have type 'Event' but here has type ''a * 'b'
How do I solve this?
I could just use let printEvents (events) but then I would have this signature: 'a list -> unit and I want to have Event list -> unit
[ created, promoted ] is a one element list of tuples of type CustomerCreated * CustomerPromoted. So the first fix would be to use the list separator ; instead of the tuple separator ,. [ created; promoted ]
Second fix is to note that discriminated unions aren't aliases. Created is of type Event that contains data of the type CustomerCreated. So changing let created = { id = "1"; companyName = "MS" } to let created = Created { id = "1"; companyName = "MS" } would create the type you want.
I have a type 'Team' which contains another type 'Employee'. I have overridden the ToString() for the type 'Employee'. However, when I do ToString() for the type 'Team', the details from 'Employee' is pretty-printed with the standard ToString() implementation and my overriding logic was never used. Can someone help understand why the override didn't work? Here is the code:
type Employee =
{
name : string
address : string
}
override this.ToString() = sprintf "Hello %s" this.name
type Team =
{
employee1 : Employee
}
with member this.ToTightString =
this.ToString().Replace(" ","")
let employee = { name="Bob"; address="Unknown"; }
let team = {employee1=employee}
printfn "%s" (employee.ToString()) // Override works!
// OUTPUT: Hello Bob
printfn "--------------------"
printf "%s" team.ToTightString // Override doesn't work
// OUTPUT: {employee1={name="Bob";address="Unknown";};}
As #rmunn has said above, the textual representation of a type (say, type1) specified in StructuredFormatDisplay is retained even if one calls ToString() on a type that contains the 'type1' type. Here's an example:
open System.Text.RegularExpressions
[<StructuredFormatDisplay("name=Always Harry address={address}")>]
type Employee =
{
name : string
address : string
}
type AddressContainer =
{
employee: Employee
containerName: string
}
let address1 = { name="Bob"; address="Random City" }
let addressContainer1 = { employee=address1; containerName= "container1"}
printf "%s" (address1.ToString()) // prints "name=Always Harry address=Random City"
printf "%s" (addressContainer1.ToString()) // prints {employee = name=Always Harry address=Random City; containerName = "container1";}
I am playing around with F# and wanted to check how it generates code compared to C# and found a strange line.
I am using dotTrace to decompile code and make C# equivalent. I have also tried to check IL code using LinqPad.
My code is quite small.
open System
[<EntryPoint>]
let main argv =
let mutable sum = 0
// 1000 or 997
//let arr : int array = Array.zeroCreate 997
//let arr = Enumerable.Range(0, 997).ToArray()
let arr :int array = [|0..997|]
arr |> Array.iter (fun x -> sum <- sum + x)
printfn "%i" sum
0
And this is what I get.
{
int func = 0;
int[] numArray = SeqModule.ToArray<int>(Operators.CreateSequence<int>(Operators.OperatorIntrinsics.RangeInt32(0, 1, 997)));
if ((object) numArray == null)
throw new ArgumentNullException("array");
int length = numArray.Length;
int index = 0;
int num1 = length - 1;
if (num1 >= index)
{
do
{
int num2 = numArray[index];
func += num2;
++index;
}
while (index != num1 + 1);
}
PrintfModule.PrintFormatLineToTextWriter<FSharpFunc<int, Unit>>(Console.Out, (PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit>) new PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit, int>("%i")).Invoke(func);
return 0;
}
}
And this is how IL looks like.
// IL_0019: stloc.1 // 'numArray [Range(Instruction(IL_0019 stloc.1)-Instruction(IL_0040 ldloc.1))]'
// IL_001a: ldloc.1 // 'numArray [Range(Instruction(IL_0019 stloc.1)-Instruction(IL_0040 ldloc.1))]'
// IL_001b: box int32[]
// IL_0020: brfalse.s IL_0025
// IL_0022: nop
// IL_0023: br.s IL_0030
// IL_0025: ldstr "array"
// IL_002a: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string)
// IL_002f: throw
Compiled using Release, .Net 4.6, FSharp.Core 4.4.0.0, Optimize code, Generate Tail Calls.
I am very curious about the NULL check and cast.
(object) numArray == null
I do understand why the obj cast is done. The array is not a null and can't be checked without.
I am curious (don't thinks it is a problem) and the question is more about compiler.
Why would it be useful to check for null? I am not defining an option type.
Under what conditions the exception will fire.
That check is part of the implementation of Array.iter. The compiler just seems not to be smart enough to figure out that box arg in this case is never going to be null.
I'm trying to replace chained String.Replace() calls with a more functional version. Original:
let ShortenRomanNumeral (num : string) : string =
num.Replace("VIIII", "IX").Replace("IIII", "IV").Replace("LXXXX", "XC").Replace("XXXX", "XL").Replace("DCCCC", "CM").Replace("CCCC", "CD")
Functional version that works with one key value pair:
let ShortenRomanNumeral' (str : string) (k : string) (v : string) : string =
let strAfterReplace =
str.Replace(k, v)
strAfterReplace
I'm struggling to extend it to work with a list of tuples, such as
let replacements = [("VIIII", "IX"); ("IIII", "IV"); ...]
How can I write this function to apply the Replace() to the string for each key and value in the replacements list?
Fold is good. But just to demonstrate another way to do it...
// You can put the input string
// as the LAST parameter not first
let shortenRomanNumeral (k:string,v:string) (input:string) =
input.Replace(k,v)
// This allows you to do partial application like this
let replace4 = shortenRomanNumeral ("IIII", "IV")
let replace9 = shortenRomanNumeral ("VIIII", "IX")
// replace9 and replace4 have the signature string->string
// they are now simple string transformation functions
replace4 "abcIIIIdef" |> printfn "result is '%s'"
replace9 "abcVIIIIdef" |> printfn "result is '%s'"
// and they can be composed together.
// Order is important. Do 9 before 4.
let replace4and9 = replace9 >> replace4
replace4and9 "VIIII abc IIII" |> printfn "result is '%s'"
// With this approach, you can now transform a list of tuples
// into a list of string transforms using List.map
let listOfTransforms =
[("VIIII", "IX"); ("IIII", "IV");]
|> List.map shortenRomanNumeral
// and you can combine all these into one big transformation
// function using composition
let transformAll =
listOfTransforms
|> List.reduce (>>)
// finally you can apply the big function
transformAll "VIIII abc IIII" |> printfn "result is '%s'"
A fold will do the job:
let ShortenRomanNumeral' (str : string) (k : string, v : string) : string =
let strAfterReplace =
str.Replace(k, v)
strAfterReplace
let replacements = [("VIIII", "IX"); ("IIII", "IV"); ]
let replaceValues str = List.fold ShortenRomanNumeral' str replacements
replaceValues "VI VII VIIII I II III IIII" // "VI VII IX I II III IV"
Note that I only modified the last parameter of ShortenRomanNumeral' to accept tupled values.
How do I go about using the TryTake method on a BlockingCollection<'a> passing in a timeout period in milliseconds?
Heres the signature:
BlockingCollection.TryTake(item: byref, millisecondsTimeout: int) : bool
is it possible to use the Tuple method of avoiding passing a ref type like on the Dictionary.TryGet methods?
i.e.
let success, item = myDictionary.TryGetValue(client)
Im struggling with this particular signature, any suggestions would be great.
Cheers!
I believe that you can only use that technique for byref parameters which occur at the end of the parameter list (this is similar to the rule for optional parameters). So if BlockingCollection.TryTake were defined with signature int * 'T byref -> bool it would work, but since it's defined as 'T byref * int -> bool it won't.
For example:
open System.Runtime.InteropServices
type T =
static member Meth1(a:int, [<Out>]b:string byref, [<Out>]c:bool byref) : char =
b <- sprintf "%i" a
c <- a % 2 = 0
char a
static member Meth2([<Out>]b:string byref, [<Out>]c:bool byref, a:int) : char =
b <- sprintf "%i" a
c <- a % 2 = 0
char a
// ok
let (r,b,c) = T.Meth1(5)
// ok
let (r,c) = T.Meth1(5,ref "test")
// ok
let r = T.Meth1(5, ref "test", ref true)
// doesn't compile
let (r,b,c) = T.Meth2(5)
// doesn't compile
let (r,c) = T.Meth2(ref "test", 5)
// ok
let r = T.Meth2(ref "test", ref true, 5)