Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .fantomasignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ src/Compiler/SyntaxTree/LexFilter.fs

src/FSharp.Core/array2.fs
src/FSharp.Core/array3.fs
src/FSharp.Core/fslib-extra-pervasives.fs
src/FSharp.Core/Linq.fs
src/FSharp.Core/local.fs
src/FSharp.Core/nativeptr.fs
Expand Down
3 changes: 3 additions & 0 deletions docs/release-notes/.FSharp.Core/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
* Fix `Array.exists2` documentation examples to use equal-length arrays; the previous examples would throw `ArgumentException` at runtime instead of returning the documented `false`/`true` values. ([PR #19672](https://github.com/dotnet/fsharp/pull/19672))
* Move `Async.StartChild` to the "Starting Async Computations" docs category alongside `Async.StartChildAsTask`. ([Issue #19667](https://github.com/dotnet/fsharp/issues/19667))
* Add `InlineIfLambda` to `Array.init` ([PR #19869](https://github.com/dotnet/fsharp/pull/19869))
### Added

* Added generic `print` and `printn` functions (`'T -> unit`) to `ExtraTopLevelOperators` for simple value printing to stdout. ([RFC FS-1125](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1125-print-printn-functions.md), [PR #19265](https://github.com/dotnet/fsharp/pull/19265))
17 changes: 17 additions & 0 deletions src/FSharp.Core/fslib-extra-pervasives.fs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,23 @@ module ExtraTopLevelOperators =
let eprintfn format =
Printf.eprintfn format

[<CompiledName("PrintValue")>]
let inline print (value: 'T) =
Console.Out.Write(string value)
// Culture-independent types can bypass 'string' and use TextWriter overloads directly.
// Numeric types must go through 'string' to ensure InvariantCulture formatting,
// since TextWriter.Write(int/float/...) uses the writer's FormatProvider (CurrentCulture).
when 'T : string = Console.Out.Write((# "" value : string #))
when 'T : char = Console.Out.Write((# "" value : char #))
when 'T : bool = Console.Out.Write((# "" value : bool #))

[<CompiledName("PrintValueLine")>]
let inline printn (value: 'T) =
Console.Out.WriteLine(string value)
when 'T : string = Console.Out.WriteLine((# "" value : string #))
when 'T : char = Console.Out.WriteLine((# "" value : char #))
when 'T : bool = Console.Out.WriteLine((# "" value : bool #))

Comment thread
T-Gro marked this conversation as resolved.
[<CompiledName("DefaultAsyncBuilder")>]
let async = AsyncBuilder()

Expand Down
27 changes: 27 additions & 0 deletions src/FSharp.Core/fslib-extra-pervasives.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,33 @@ module ExtraTopLevelOperators =
[<CompiledName("PrintFormatLineToError")>]
val eprintfn: format: Printf.TextWriterFormat<'T> -> 'T

/// <summary>Converts the value to a string using the <c>string</c> operator and writes it to the standard output.</summary>
///
/// <param name="value">The value to print.</param>
///
/// <example id="print-example">
/// <code lang="fsharp">
/// print "Hello, "
/// print "World!"
/// // output: Hello, World!
/// </code>
/// </example>
[<CompiledName("PrintValue")>]
val inline print: value: 'T -> unit

/// <summary>Converts the value to a string using the <c>string</c> operator and writes it to the standard output, followed by a newline.</summary>
///
/// <param name="value">The value to print.</param>
///
/// <example id="printn-example">
/// <code lang="fsharp">
/// printn "Hello, World!"
/// // output: Hello, World!
/// </code>
/// </example>
[<CompiledName("PrintValueLine")>]
val inline printn: value: 'T -> unit

/// <summary>Print to a string using the given format.</summary>
///
/// <param name="format">The formatter.</param>
Expand Down
98 changes: 98 additions & 0 deletions tests/FSharp.Compiler.ComponentTests/EmittedIL/PrintFunction.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace EmittedIL

open Xunit
open FSharp.Test.Compiler

module PrintFunction =

[<Fact>]
let ``print with int specializes to Int32 ToString with InvariantCulture``() =
FSharp """
module PrintInt

let printInt () = print 42
"""
|> withOptimize
|> compile
|> shouldSucceed
|> verifyIL [
"""call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()"""
"""call class [netstandard]System.Globalization.CultureInfo [netstandard]System.Globalization.CultureInfo::get_InvariantCulture()"""
"""call instance string [netstandard]System.Int32::ToString(string,"""
"""callvirt instance void [netstandard]System.IO.TextWriter::Write(string)"""]

[<Fact>]
let ``printn with int specializes to Int32 ToString with InvariantCulture``() =
FSharp """
module PrintnInt

let printnInt () = printn 42
"""
|> withOptimize
|> compile
|> shouldSucceed
|> verifyIL [
"""call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()"""
"""call class [netstandard]System.Globalization.CultureInfo [netstandard]System.Globalization.CultureInfo::get_InvariantCulture()"""
"""call instance string [netstandard]System.Int32::ToString(string,"""
"""callvirt instance void [netstandard]System.IO.TextWriter::WriteLine(string)"""]

[<Fact>]
let ``print with string calls Write directly``() =
FSharp """
module PrintString

let printStr () = print "hello"
"""
|> withOptimize
|> compile
|> shouldSucceed
|> verifyIL [
"""call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()"""
"""callvirt instance void [netstandard]System.IO.TextWriter::Write(string)"""]

[<Fact>]
let ``print with char calls Write char overload``() =
FSharp """
module PrintChar

let printChar () = print 'A'
"""
|> withOptimize
|> compile
|> shouldSucceed
|> verifyIL [
"""call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()"""
"""callvirt instance void [netstandard]System.IO.TextWriter::Write(char)"""]

[<Fact>]
let ``print with bool calls Write bool overload``() =
FSharp """
module PrintBool

let printBool () = print true
"""
|> withOptimize
|> compile
|> shouldSucceed
|> verifyIL [
"""call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()"""
"""callvirt instance void [netstandard]System.IO.TextWriter::Write(bool)"""]

[<Fact>]
let ``printn with float specializes to Double ToString with InvariantCulture``() =
FSharp """
module PrintnFloat

let printnFloat () = printn 3.14
"""
|> withOptimize
|> compile
|> shouldSucceed
|> verifyIL [
"""call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()"""
"""call class [netstandard]System.Globalization.CultureInfo [netstandard]System.Globalization.CultureInfo::get_InvariantCulture()"""
"""call instance string [netstandard]System.Double::ToString(string,"""
"""callvirt instance void [netstandard]System.IO.TextWriter::WriteLine(string)"""]
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
<Compile Include="EmittedIL\DebugPointsOnStack\DebugPointsOnStack.fs" />
<Compile Include="EmittedIL\Enums.fs" />
<Compile Include="EmittedIL\Literals.fs" />
<Compile Include="EmittedIL\PrintFunction.fs" />
<Compile Include="EmittedIL\NoCompilerInlining.fs" />
<Compile Include="EmittedIL\RealInternalSignature\ModuleVisibility.fs" />
<Compile Include="EmittedIL\RealInternalSignature\ModuleInitialization.fs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,8 @@ Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToStringThenFail[T,TR
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToString[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValue[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValueLine[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceExpression[T](Microsoft.FSharp.Quotations.FSharpExpr`1[T])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceUntypedExpression[T](Microsoft.FSharp.Quotations.FSharpExpr)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T[,] CreateArray2D[a,T](System.Collections.Generic.IEnumerable`1[a])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,8 @@ Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToStringThenFail[T,TR
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToString[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValue[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValueLine[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceExpression[T](Microsoft.FSharp.Quotations.FSharpExpr`1[T])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceUntypedExpression[T](Microsoft.FSharp.Quotations.FSharpExpr)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T[,] CreateArray2D[a,T](System.Collections.Generic.IEnumerable`1[a])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,8 @@ Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToStringThenFail[T,TR
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToString[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValue[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValueLine[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceExpression[T](Microsoft.FSharp.Quotations.FSharpExpr`1[T])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceUntypedExpression[T](Microsoft.FSharp.Quotations.FSharpExpr)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T[,] CreateArray2D[a,T](System.Collections.Generic.IEnumerable`1[a])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,8 @@ Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToStringThenFail[T,TR
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToString[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValue[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: Void PrintValueLine[T](T)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceExpression[T](Microsoft.FSharp.Quotations.FSharpExpr`1[T])
Microsoft.FSharp.Core.ExtraTopLevelOperators: T SpliceUntypedExpression[T](Microsoft.FSharp.Quotations.FSharpExpr)
Microsoft.FSharp.Core.ExtraTopLevelOperators: T[,] CreateArray2D[a,T](System.Collections.Generic.IEnumerable`1[a])
Expand Down
1 change: 1 addition & 0 deletions tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<None Include="FSharp.Core\Microsoft.FSharp.Core\IntConversionsTestGenerator.fsx" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\OptionModule.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\PrintfTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\PrintTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\ResultTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Core\ExtraTopLevelOperatorsTests.fs" />
<Compile Include="FSharp.Core\Microsoft.FSharp.Control\EventTypes.fs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

// Various tests for:
// Microsoft.FSharp.Core.ExtraTopLevelOperators.print
// Microsoft.FSharp.Core.ExtraTopLevelOperators.printn

namespace FSharp.Core.UnitTests

open System
open System.IO
open Xunit

[<Collection(nameof FSharp.Test.NotThreadSafeResourceCollection)>]
type PrintTests() =

let captureConsoleOut (f: unit -> unit) =
let oldOut = Console.Out
use sw = new StringWriter()
Console.SetOut(sw)
try
f ()
sw.ToString()
finally
Console.SetOut(oldOut)

[<Fact>]
member _.``print writes string value``() =
let result = captureConsoleOut (fun () -> print "hello")
Assert.Equal("hello", result)

[<Fact>]
member _.``print writes integer value``() =
let result = captureConsoleOut (fun () -> print 42)
Assert.Equal("42", result)

[<Fact>]
member _.``print writes float with InvariantCulture``() =
let result = captureConsoleOut (fun () -> print 3.14)
Assert.Equal("3.14", result)

[<Fact>]
member _.``print writes bool value``() =
let result = captureConsoleOut (fun () -> print true)
Assert.Equal("True", result)

[<Fact>]
member _.``print writes Some value``() =
let result = captureConsoleOut (fun () -> print (Some 42))
Assert.Equal("Some(42)", result)

[<Fact>]
member _.``print writes None value``() =
let result = captureConsoleOut (fun () -> print None)
Assert.Equal("", result)

[<Fact>]
member _.``print writes list value``() =
let result = captureConsoleOut (fun () -> print [1; 2; 3])
Assert.Equal("[1; 2; 3]", result)

[<Fact>]
member _.``printn writes value followed by newline``() =
let result = captureConsoleOut (fun () -> printn "hello")
Assert.Equal("hello" + Environment.NewLine, result)

[<Fact>]
member _.``multiple prints concatenate``() =
let result = captureConsoleOut (fun () ->
print "Hello, "
print "World!")
Assert.Equal("Hello, World!", result)

[<Fact>]
member _.``printn writes integer with newline``() =
let result = captureConsoleOut (fun () -> printn 42)
Assert.Equal("42" + Environment.NewLine, result)
Loading