I wrote an fsi script that worked great and wanted to compile it so that I could move it easier. However, when I compiled it, all of a sudden FileHelpers began giving errors.
The following code uses FileHelpers 2.9.9. It is a minimal working example to illustrate the issue, test.fsx
:
#r "FileHelpers.dll"
open FileHelpers
[<DelimitedRecord(",")>]
type Type =
val field1 : string
val field2 : int
override x.ToString() = sprintf "%s: %d" x.field1 x.field2
let readFile<'a> file = seq {
use engine1 = new FileHelperAsyncEngine(typeof<'a>)
use tmp1 = engine1.BeginReadFile(file)
engine1.ReadNext() |> ignore
while engine1.LastRecord <> null do
yield engine1.LastRecord :?> 'a
engine1.ReadNext() |> ignore
}
readFile<Type> "test.csv" |> Seq.iter (printfn "%A")
with the file test.csv
as
test1,1
test2,2
test3,3
If I run the code as fsi .\test.fsx
it will work fine. However, if I try to compile it with fsc .\test.fsx
and run .\test.exe
I get the error Unhandled Exception: FileHelpers.BadUsageException: The record class Type needs a constructor with no args (public or private)
. A work around that works in both scripting and compiled mode is
[<DelimitedRecord(",")>]
type Type () =
[<DefaultValue>]
val mutable field1 : string
[<DefaultValue>]
val mutable field2 : int
override x.ToString() = sprintf "%s: %d" x.field1 x.field2
Why would it work as a script but not compiled? I would like to keep it immutable if possible. Thanks for any insight!
FSI uses System.Reflection.Emit to compile your F# code on-the-fly. It appears that types generated with System.Reflection.Emit always have at least one constructor (either the default public constructor or an explicitly defined constructor). Thus it isn't easily possible for the code emitted by FSI to exactly imitate the result of the compiled code, which has no constructors at all (neither public nor private).