Skip to content

Commit ec474ef

Browse files
github-actions[bot]Repo AssistCopilotdsyme
authored
[Repo Assist] Add PreferFloats static parameter to CsvProvider (#1655)
* Add PreferFloats static parameter to CsvProvider Adds a new boolean static parameter `PreferFloats` (default: `false`) to `CsvProvider` that causes the type inference to use `float` instead of `decimal` when a column's values can be parsed as either. This addresses the long-standing feature request in issue #838. The existing behaviour (inferring `decimal`) is preserved by default; set `PreferFloats = true` to opt in to `float` inference. Implementation: - `StructuralInference.fs`: add `preferFloats: bool` parameter to `inferPrimitiveType`; guard the decimal match arm with `when not preferFloats`; add internal helper `getInferedTypeFromStringPreferFloats`. - `CsvInference.fs`: thread `preferFloats` through `inferCellType`, `inferType`, `inferColumnTypes`, and `CsvFile.InferColumnTypes`. - `HtmlInference.fs`: pass `false` for the new parameter (HTML provider is unaffected). - `CsvProvider.fs`: register the new static parameter at index 19. - `TypeProviderInstantiation.fs`: update `CsvProviderArgs` record and args array for the design-time test harness. - `CsvProvider.fs` (tests): add two tests covering the new parameter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger CI checks --------- Co-authored-by: Repo Assist <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Don Syme <dsyme@users.noreply.github.com>
1 parent 1e61964 commit ec474ef

7 files changed

Lines changed: 56 additions & 11 deletions

File tree

src/FSharp.Data.Csv.Core/CsvInference.fs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ let internal inferCellType
125125
missingValues
126126
inferenceMode
127127
strictBooleans
128+
preferFloats
128129
cultureInfo
129130
unit
130131
(value: string)
@@ -140,7 +141,15 @@ let internal inferCellType
140141
InferedType.Null
141142
else
142143
let inferedType =
143-
StructuralInference.getInferedTypeFromString unitsOfMeasureProvider inferenceMode cultureInfo value unit
144+
if preferFloats then
145+
StructuralInference.getInferedTypeFromStringPreferFloats
146+
unitsOfMeasureProvider
147+
inferenceMode
148+
cultureInfo
149+
value
150+
unit
151+
else
152+
StructuralInference.getInferedTypeFromString unitsOfMeasureProvider inferenceMode cultureInfo value unit
144153

145154
if strictBooleans then
146155
// With StrictBooleans=true, only "true"/"false" trigger bool inference.
@@ -305,6 +314,7 @@ let internal inferType
305314
missingValues
306315
inferenceMode
307316
strictBooleans
317+
preferFloats
308318
cultureInfo
309319
assumeMissingValues
310320
preferOptionals
@@ -358,6 +368,7 @@ let internal inferType
358368
missingValues
359369
inferenceMode
360370
strictBooleans
371+
preferFloats
361372
cultureInfo
362373
unit
363374
value
@@ -456,6 +467,7 @@ let internal inferColumnTypes
456467
missingValues
457468
inferenceMode
458469
strictBooleans
470+
preferFloats
459471
cultureInfo
460472
assumeMissingValues
461473
preferOptionals
@@ -469,6 +481,7 @@ let internal inferColumnTypes
469481
missingValues
470482
inferenceMode
471483
strictBooleans
484+
preferFloats
472485
cultureInfo
473486
assumeMissingValues
474487
preferOptionals
@@ -497,7 +510,8 @@ type CsvFile with
497510
assumeMissingValues,
498511
preferOptionals,
499512
unitsOfMeasureProvider,
500-
?strictBooleans
513+
?strictBooleans,
514+
?preferFloats
501515
) =
502516

503517
let headerNamesAndUnits, schema =
@@ -511,6 +525,7 @@ type CsvFile with
511525
missingValues
512526
inferenceMode
513527
(defaultArg strictBooleans false)
528+
(defaultArg preferFloats false)
514529
cultureInfo
515530
assumeMissingValues
516531
preferOptionals

src/FSharp.Data.DesignTime/Csv/CsvProvider.fs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =
5858
let preferDateOnly = args.[16] :?> bool
5959
let strictBooleans = args.[17] :?> bool
6060
let useOriginalNames = args.[18] :?> bool
61+
let preferFloats = args.[19] :?> bool
6162

6263
// This provider already has a schema mechanism, so let's disable inline schemas.
6364
let inferenceMode = InferenceMode'.ValuesOnly
@@ -116,7 +117,8 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =
116117
assumeMissingValues,
117118
preferOptionals,
118119
unitsOfMeasureProvider,
119-
strictBooleans
120+
strictBooleans,
121+
preferFloats
120122
)
121123
#if NET6_0_OR_GREATER
122124
if preferDateOnly && ProviderHelpers.runtimeSupportsNet6Types cfg.RuntimeAssembly then
@@ -241,7 +243,8 @@ type public CsvProvider(cfg: TypeProviderConfig) as this =
241243
ProvidedStaticParameter("EmbeddedResource", typeof<string>, parameterDefaultValue = "")
242244
ProvidedStaticParameter("PreferDateOnly", typeof<bool>, parameterDefaultValue = false)
243245
ProvidedStaticParameter("StrictBooleans", typeof<bool>, parameterDefaultValue = false)
244-
ProvidedStaticParameter("UseOriginalNames", typeof<bool>, parameterDefaultValue = false) ]
246+
ProvidedStaticParameter("UseOriginalNames", typeof<bool>, parameterDefaultValue = false)
247+
ProvidedStaticParameter("PreferFloats", typeof<bool>, parameterDefaultValue = false) ]
245248

246249
let helpText =
247250
"""<summary>Typed representation of a CSV file.</summary>

src/FSharp.Data.Html.Core/HtmlInference.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ let internal inferColumns parameters (headerNamesAndUnits: _[]) rows =
2828
parameters.MissingValues
2929
parameters.InferenceMode
3030
false
31+
false
3132
parameters.CultureInfo
3233
assumeMissingValues
3334
parameters.PreferOptionals

src/FSharp.Data.Runtime.Utilities/StructuralInference.fs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ let inferPrimitiveType
525525
(unitsOfMeasureProvider: IUnitsOfMeasureProvider)
526526
(inferenceMode: InferenceMode')
527527
(cultureInfo: CultureInfo)
528+
(preferFloats: bool)
528529
(value: string)
529530
(desiredUnit: Type option)
530531
=
@@ -573,7 +574,7 @@ let inferPrimitiveType
573574
makePrimitive typeof<DateOnly>
574575
#endif
575576
| Parse TextConversions.AsDateTime date when not (isFakeDate date value) -> makePrimitive typeof<DateTime>
576-
| Parse TextConversions.AsDecimal _ -> makePrimitive typeof<decimal>
577+
| Parse TextConversions.AsDecimal _ when not preferFloats -> makePrimitive typeof<decimal>
577578
| Parse (TextConversions.AsFloat [||] false) _ -> makePrimitive typeof<float>
578579
| Parse asGuid _ -> makePrimitive typeof<Guid>
579580
| _ -> None
@@ -622,7 +623,11 @@ let inferPrimitiveType
622623
/// Infers the type of a simple string value
623624
[<Obsolete("This API will be made internal in a future release. Please file an issue at https://github.com/fsprojects/FSharp.Data/issues/1458 if you need this public.")>]
624625
let getInferedTypeFromString unitsOfMeasureProvider inferenceMode cultureInfo value unit =
625-
inferPrimitiveType unitsOfMeasureProvider inferenceMode cultureInfo value unit
626+
inferPrimitiveType unitsOfMeasureProvider inferenceMode cultureInfo false value unit
627+
628+
/// Infers the type of a simple string value, preferring float over decimal
629+
let internal getInferedTypeFromStringPreferFloats unitsOfMeasureProvider inferenceMode cultureInfo value unit =
630+
inferPrimitiveType unitsOfMeasureProvider inferenceMode cultureInfo true value unit
626631

627632
#if NET6_0_OR_GREATER
628633
/// Replaces DateOnly → DateTime and TimeOnly → TimeSpan throughout an InferedType tree.

tests/FSharp.Data.DesignTime.Tests/InferenceTests.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ let internal unitsOfMeasureProvider = ProviderHelpers.unitsOfMeasureProvider
2424

2525
let internal inferType (csv:CsvFile) inferRows missingValues cultureInfo schema assumeMissingValues preferOptionals =
2626
let headerNamesAndUnits, schema = parseHeaders csv.Headers csv.NumberOfColumns schema unitsOfMeasureProvider
27-
inferType headerNamesAndUnits schema (csv.Rows |> Seq.map (fun x -> x.Columns)) inferRows missingValues inferenceMode false cultureInfo assumeMissingValues preferOptionals unitsOfMeasureProvider
27+
inferType headerNamesAndUnits schema (csv.Rows |> Seq.map (fun x -> x.Columns)) inferRows missingValues inferenceMode false false cultureInfo assumeMissingValues preferOptionals unitsOfMeasureProvider
2828

2929
let internal toRecord fields = InferedType.Record(None, fields, false)
3030

@@ -411,7 +411,7 @@ let ``Doesn't infer 12-002 as a date``() =
411411

412412
[<Test>]
413413
let ``Doesn't infer ad3mar as a date``() =
414-
StructuralInference.inferPrimitiveType unitsOfMeasureProvider inferenceMode CultureInfo.InvariantCulture "ad3mar" None
414+
StructuralInference.inferPrimitiveType unitsOfMeasureProvider inferenceMode CultureInfo.InvariantCulture false "ad3mar" None
415415
|> should equal (InferedType.Primitive(typeof<string>, None, false, false))
416416

417417
[<Test>]

tests/FSharp.Data.DesignTime.Tests/TypeProviderInstantiation.fs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ type internal CsvProviderArgs =
2828
EmbeddedResource : string
2929
PreferDateOnly : bool
3030
StrictBooleans : bool
31-
UseOriginalNames : bool }
31+
UseOriginalNames : bool
32+
PreferFloats : bool }
3233

3334
type internal XmlProviderArgs =
3435
{ Sample : string
@@ -106,7 +107,8 @@ type internal TypeProviderInstantiation =
106107
box x.EmbeddedResource
107108
box x.PreferDateOnly
108109
box x.StrictBooleans
109-
box x.UseOriginalNames |]
110+
box x.UseOriginalNames
111+
box x.PreferFloats |]
110112
| Xml x ->
111113
(fun cfg -> new XmlProvider(cfg) :> TypeProviderForNamespaces),
112114
[| box x.Sample
@@ -246,7 +248,8 @@ type internal TypeProviderInstantiation =
246248
EmbeddedResource = ""
247249
PreferDateOnly = false
248250
StrictBooleans = false
249-
UseOriginalNames = false }
251+
UseOriginalNames = false
252+
PreferFloats = false }
250253
| "Xml" ->
251254
Xml { Sample = args.[1]
252255
SampleIsList = args.[2] |> bool.Parse

tests/FSharp.Data.Tests/CsvProvider.fs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,3 +766,21 @@ let ``CsvProvider Row With* methods do not mutate the original row`` () =
766766
let row = csv.Rows |> Seq.head
767767
let _ = row.WithName("Charlie")
768768
row.Name |> should equal "Alice"
769+
770+
// Tests for PreferFloats static parameter (issue #838)
771+
let [<Literal>] csvWithDecimals = "Name,Price,Rate\nAlice,9.99,1.5\nBob,12.50,2.7"
772+
773+
type CsvPreferFloats = CsvProvider<csvWithDecimals, PreferFloats = true>
774+
type CsvDefaultDecimal = CsvProvider<csvWithDecimals>
775+
776+
[<Test>]
777+
let ``CsvProvider PreferFloats=true infers float instead of decimal`` () =
778+
let row = CsvPreferFloats.GetSample().Rows |> Seq.head
779+
row.Price |> should equal 9.99
780+
row.Rate |> should equal 1.5
781+
782+
[<Test>]
783+
let ``CsvProvider PreferFloats=false (default) infers decimal for decimal values`` () =
784+
let row = CsvDefaultDecimal.GetSample().Rows |> Seq.head
785+
row.Price |> should equal 9.99m
786+
row.Rate |> should equal 1.5m

0 commit comments

Comments
 (0)