Skip to content

Add generic print and println functions to FSharp.Core#19265

Open
bbatsov wants to merge 4 commits intodotnet:mainfrom
bbatsov:feature/print-println
Open

Add generic print and println functions to FSharp.Core#19265
bbatsov wants to merge 4 commits intodotnet:mainfrom
bbatsov:feature/print-println

Conversation

@bbatsov
Copy link
Copy Markdown
Contributor

@bbatsov bbatsov commented Feb 9, 2026

Summary

This is another take on RFC FS-1125, following up on the closed #13597. I guess that's mostly an attempt to see if anyone's interested in driving the old proposal to the finish line. For me those would definitely be handy functions to have, but obviously they are not something very important.

The key difference from the original PR is that print and println are generic ('T -> unit) rather than string -> unit, addressing the feedback from @dsyme that a generic print function would be a better use of the "good name":

let print (value: 'T) = Console.Out.Write(string value)
let println (value: 'T) = Console.Out.WriteLine(string value)

Both functions use the existing string operator for conversion, which already:

  • Uses InvariantCulture for IFormattable types (consistent with printfn, sprintf, etc.)
  • Passes strings through as-is
  • Falls back to .ToString() for other types (F# types like Option, List, etc. have good .ToString() overrides)

Changes

  • Added print and println signatures with XML docs to fslib-extra-pervasives.fsi
  • Added implementations in fslib-extra-pervasives.fs
  • Added unit tests (PrintTests.fs) covering: string, int, float, bool, Option, None, list, newline behavior, and concatenation
  • Updated all 4 surface area baselines

Open questions

  • What about eprintf(n) and fprintf(n)? Do they generic counterparts as well?

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 9, 2026

❗ Release notes required


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/FSharp.Core docs/release-notes/.FSharp.Core/11.0.100.md

Comment thread src/FSharp.Core/fslib-extra-pervasives.fs
@bbatsov bbatsov force-pushed the feature/print-println branch from 53c25f7 to c00b32e Compare February 14, 2026 13:30
@vzarytovskii
Copy link
Copy Markdown
Member

Will also probably want IL baseline tests to make sure correct overloads are getting called.

@bbatsov
Copy link
Copy Markdown
Contributor Author

bbatsov commented Feb 14, 2026

I've added some IL tests - hopefully I got those right. First contributions as always the hardest... 😅

@vzarytovskii
Copy link
Copy Markdown
Member

vzarytovskii commented Feb 14, 2026

I've added some IL tests - hopefully I got those right. First contributions as always the hardest... 😅

Yeah, those are the ones I meant. However, I do see that for some of them it is calling ToString for some value types (i32 for example), and calls string overload for the Write(Line)

@brianrourkeboll
Copy link
Copy Markdown
Contributor

However, I do see that for some of them it is calling ToString for some value types (i32 for example), and calls string overload for the Write(Line)

That's inherent to the approach taken in this PR (calling string on the input and passing it to Console.WriteLine), no?

We could delegate to the appropriate overload of Console.WriteLine at compile time by using static optimizations for common types (like the string function itself does).

@vzarytovskii
Copy link
Copy Markdown
Member

vzarytovskii commented Feb 14, 2026

However, I do see that for some of them it is calling ToString for some value types (i32 for example), and calls string overload for the Write(Line)

That's inherent to the approach taken in this PR (calling string on the input and passing it to Console.WriteLine), no?

Yes, I forgot to look at the implementation tbh.

We could delegate to the appropriate overload of Console.WriteLine at compile time by using static optimizations for common types (like the string function itself does).

We probably should, jit might lift some allocations to stack, but we shouldn't rely on it.

@bbatsov
Copy link
Copy Markdown
Contributor Author

bbatsov commented Feb 14, 2026

I think I'll need some more pointers about the next step, as I'm not quite sure how to proceed from here.

@Happypig375
Copy link
Copy Markdown
Member

@bbatsov consider looking at the implementation for the string function and use a similar syntax for Console.WriteLine overloads.

@bbatsov
Copy link
Copy Markdown
Contributor Author

bbatsov commented Feb 15, 2026

I've added static optimizations for string, char, and bool — these types are culture-independent, so we can call the corresponding TextWriter.Write/WriteLine overload directly, bypassing the string operator entirely.

For numeric types (int, float, decimal, etc.), we must continue going through the string operator to ensure InvariantCulture formatting. TextWriter.Write(int) internally calls value.ToString(FormatProvider), and Console.Out.FormatProvider is CultureInfo.CurrentCulture — so delegating directly would break InvariantCulture consistency for negative numbers and floating-point values in some locales.

The IL tests now verify:

  • int/floatToString(string, IFormatProvider) with InvariantCulture + Write(string)
  • string → direct Write(string) (no null check overhead)
  • char → direct Write(char)
  • bool → direct Write(bool)

I hope I got those right. I never thought a couple of print functions could be so involved. 😅

@bbatsov bbatsov force-pushed the feature/print-println branch from 485c8dc to 43b247f Compare March 5, 2026 10:35
@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Mar 6, 2026
@T-Gro T-Gro enabled auto-merge (squash) March 6, 2026 22:10
auto-merge was automatically disabled March 16, 2026 14:01

Head branch was pushed to by a user without write access

@bbatsov bbatsov force-pushed the feature/print-println branch 3 times, most recently from 804bf64 to 2db7c71 Compare March 16, 2026 14:46
bbatsov added 3 commits March 16, 2026 16:49
Add inline `print: 'T -> unit` and `println: 'T -> unit` to
ExtraTopLevelOperators. These use the existing `string` function for
conversion (InvariantCulture for IFormattable, .ToString() fallback)
and write to Console.Out.

RFC FS-1125
Verify that inline specialization produces direct calls to
Int32.ToString, Double.ToString with InvariantCulture, and
Console.Out.Write/WriteLine for the respective types.
For culture-independent types (string, char, bool), bypass the
`string` operator and call the appropriate TextWriter.Write/WriteLine
overload directly.

Numeric types (int, float, etc.) must still go through `string` to
ensure InvariantCulture formatting, since TextWriter.Write(int/float)
uses the writer's FormatProvider which is CurrentCulture for Console.Out.

Add IL tests verifying char and bool use their direct overloads.
@bbatsov bbatsov force-pushed the feature/print-println branch from 2db7c71 to 5833768 Compare March 16, 2026 14:50
@bbatsov
Copy link
Copy Markdown
Contributor Author

bbatsov commented Mar 16, 2026

In case that matters - I rebased the PR on top of the current main. Also - should the release notes be in the file for F# 10 or 11? The CI checks seems to look for them in 11.

@T-Gro
Copy link
Copy Markdown
Member

T-Gro commented Mar 17, 2026

Release notes for 11 (it will now be more stable for requiring 11 for about half a year).

@bbatsov
Copy link
Copy Markdown
Contributor Author

bbatsov commented Mar 17, 2026

Just moved the release notes to (hopefully) the right place.

@T-Gro T-Gro enabled auto-merge (squash) March 17, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants