I've started learning F# and functional programming in general, but the code I wrote doesn't seems to be really functional. Could you take a look and say how to make it more as it should be in functional programming?
type internal CsvReader(source : Stream) =
// input stream
let stream = source
// reader
let mutable reader : StreamReader = new StreamReader(source)
[<Literal>]
let bufferSize = 1000
// currently processed line
let mutable buffer : char[] = Array.zeroCreate bufferSize
// current position
let mutable currentPosition : int = bufferSize
// end of file
let mutable EndOfFile : bool = false
// loads new chunk of data from file to buffer
let UpdateBuffer() : bool =
match reader.ReadBlock(buffer, 0, buffer.Length) with
| 0 -> false
| _ -> true
// initialization method
let Initialize() =
let pos = source.Seek(0L, SeekOrigin.Begin)
if not (pos = 0L) then raise(Exception("sdf"))
()
// read one char from stream
let rec GetChar(eat : bool) : (bool * char) =
let position = currentPosition
match position with
| 1000 ->
match UpdateBuffer() with
| false ->
EndOfFile <- true
(false, Char.MinValue)
| true -> currentPosition <- 0; GetChar(eat)
| _ ->
let item = System.Convert.ToInt32(buffer.[position])
match item with
| 0 -> (false, Char.MinValue)
| _ ->
match eat with
| true ->
currentPosition <- currentPosition + 1;
(true, buffer.[position])
| false ->
(true, buffer.[position])
// default constructor logic
do
if Utils.IsNull source then raise(ArgumentNullException("source"))
if not source.CanRead then raise(ArgumentException("source"))
if not source.CanSeek then raise(ArgumentException("source"))
Initialize()
()
// parameterless constructor
// always throws ArgumentNullException
private new() = CsvReader(null)
// just for testing
member x.PublicGetChar = GetChar
Right now the only think it does it stream content of Stream
char by char by PublicGetChar
method. I wrote UT to make sure it works:
[<TestClass>]
type CsvReaderTests() =
let GetStringStream(input : string) : Stream =
let stream = new MemoryStream()
let writer = new StreamWriter(stream)
ignore(writer.Write(input))
writer.Flush()
stream.Position <- 0L
stream :> Stream
[<TestMethod>]
member this.TestMethod1 () =
let input = "test"
let reader = new CsvReader(GetStringStream(input))
let chars = seq { while fst(reader.PublicGetChar(false)) do yield snd(reader.PublicGetChar(true)) }
let output = (new System.String(chars |> Array.ofSeq))
input |> should equal output
Main concerns I have about the code:
- usage of
match
/with
- a lot of
mutable
stuff withinCsvReader
type - if I had to write it in C# it would look really similar :D
That's the very first F# code I've ever written, so be polite, please :)
StreamReader.Read()
? \$\endgroup\$Read
would require file access every time you call it. With this code you read from file in chunks, which should be faster. \$\endgroup\$StreamReader
already has an internal buffer. Anyway, whenever you do something that “should be faster”, you should really measure that it actually is faster. \$\endgroup\$