dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.92k stars 786 forks source link

F#: [<StructuredFormatDisplay>] in composed records just print dots. (...) #6753

Open srlopez opened 5 years ago

srlopez commented 5 years ago

Context: Running F# in a containerized environment with dotnet 2.2.203 F# Version: $ fsharpc Microsoft (R) F# Compiler version 10.2.3 for F# 4.5

Question: The StructuredFormatDisplay in a Composed Record doesn't work.

Repro steps

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/DiskCount: %O" __.Id __.Manufacturer __.Disks

let myPc =
        { Id = 0
          Manufacturer = "Computers Inc."
          Disks =
            [ { SizeGb = 100 }
              { SizeGb = 250 }
              { SizeGb = 500 } ] }

printfn "%%O = %O" myPc 
printfn "%%A = %A" myPc   

Expected behavior

The expected behavior just print the Diks size correctly on both lines %O and %A

%O = #0<Computers Inc.>[<100GB>; <250GB>; <500GB>]
%A = Computer #0: Computers Inc./3:[100GB; 250GB; 500GB]

Actual behavior

On the %A line apears some dots (...) insted of the real value

%O = #0<Computers Inc.>[<100GB>; <250GB>; <500GB>]
%A = Computer #0: Computers Inc./3:[...GB; ...GB; ...GB]

Known workarounds

One workaround could be adding a new string property holding the formatted string of the disk list, and use that property instead: (stackoverflow)

[<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 information

Running F# in a containerized environment with dotnet 2.2.203 F# Version: $ fsharpc Microsoft (R) F# Compiler version 10.2.3 for F# 4.5

Extra issue

Additionally, in a record, if an option field without value, i.e. with None value, does not use the StructuredFormatDisplay attribute, it instead prints the contents of the record without showing error. and the option field shows it as None.

Couldn't you use the attribute and print None too? Example code: take care of the mutable field Boss, is a string option.

[<StructuredFormatDisplay("Company #{Id}: {Boss}/{Name}")>]
[<CLIMutable>]
type Company=
  { Id : int
    mutable Name : string
    mutable Boss : string option }
  with
    static member New( name: string ) = { Id = 0; Name = name; Boss = None }
....
    let myCompany = Company.New("Koséi Kantà")
    printfn "%A" myCompany

    myCompany.Boss <- Some "String"
    printfn "%A" myCompany       

Result:

{Id = 0;
 Name = "Koséi Kantà";
 Boss = None;}

 Company #0: Some "String"/Koséi Kantà

The desired behavior would be:

Company #0: None/Koséi Kantà

Thanks.

cartermp commented 4 years ago

As per the docs, this is a bug: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/plaintext-formatting#customize-plain-text-formatting-with-structuredformatdisplay

dsyme commented 4 years ago

The problem is that "print depth" limitations are kicking in very hard, even for structured formatting attributes. Setting say

fsi.PrintDepth <- 10000

fixes this. The default is 100, but recursive calls via %A are hit by a 10x penalty

This is not mentioned at https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/plaintext-formatting#large-cyclic-or-deeply-nested-values

I do think this should be fixed/tuned better. Relevant lines are

Arrow7000 commented 1 year ago

@dsyme is there a way to do fsi.PrintDepth <- 10000 in regular project .fs files or is this only available for .fsx script files?

In my project the value fsi seems to be undefined

auduchinok commented 1 year ago

In my project the value fsi seems to be undefined

@Arrow7000 fsi value is defined in FSharp.Compiler.Interactive.Settings assembly that is automatically referenced in scripts. It's a part of F# Interactive.

Arrow7000 commented 1 year ago

Right, does that mean there's no way to set the print depth on regular F# files, run as part of an assembly and not in an interactive session?

cartermp commented 1 year ago

Not that I am aware of, no. You might want to try setting <PrintDepth> in a project file and see?

Arrow7000 commented 1 year ago

Doesn't seem to do anything unfortunately

devenv_yxjfwy1SHu VsDebugConsole_xi7aauLVp9

Is there no way to override the default format options print depth in the compiler? Or is the compiler version of the formatter already 'baked in' and so can't be modified?

https://github.com/dotnet/fsharp/blob/546c0463479e59e02a006d234ef56c65fb309efd/src/Compiler/Utilities/sformat.fs#L402-L436

Arrow7000 commented 1 year ago

Alternatively... if I import the F# compiler service Nuget package into my own project could I use that to create my own custom formatter function that's exactly the same as the built in printf "%A" one, but with the PrintDepth option modified?