5
\$\begingroup\$

I'm a C# developer learning F#. I have a lot of questions about practices and principles within the code. I would like to know what a "native" F# developer would've done differently.

[<AutoOpen>]
module MyApp
module Railway = 
 // NOTE: will Tee make sense to most people? it's from this https://fsharpforfunandprofit.com/rop/ 
 let Tee func value = 
 func value
 value
module Stack =
 // NOTE: I've just made functions to give me stack-like behavior. should i make
 // a whole type for this? what might it look like?
 // NOTE: Upper or lower case function names? the built-in collections
 // all have lower, but i read that shouldn't be practiced anymore.
 // can't remember where. any thoughts?
 let Push stack value = 
 value :: stack
 let Pop stack = 
 match stack with
 | [] -> None, stack
 | [v] -> Some(v), []
 | _ -> Some(List.head stack), List.tail stack
[<AutoOpen>]
module State =
 open System.Text
 type State = { 
 Index : int
 Pointer : int 
 Stack : int list
 Input : byte list
 Memory : byte array
 }
 // NOTE: no one is forced to use this constructor function. what if they
 // initialize their own "state" improperly?
 let Create (input : string) = 
 { Index = 0
 Pointer = 0
 Stack= List.empty
 Input = ASCIIEncoding.ASCII.GetBytes(input) |> Array.toList 
 Memory = Array.init 100 (fun i -> 0uy) }
[<AutoOpen>]
module Source =
 type Instruction = 
 | INCPTR
 | DECPTR
 | INCVAL
 | DECVAL
 | OUTVAL
 | INPVAL
 | WHILE
 | WEND
 | NOOP
 // NOTE: is this translation worth it? i'm an enterprise developer at heart.
 // defining all the things always feels good, but it does add more code
 // to the mix.
 let Compile (code : string) : Instruction list =
 seq {
 for c in code do
 match c with
 | '>' -> yield INCPTR
 | '<' -> yield DECPTR
 | '+' -> yield INCVAL
 | '-' -> yield DECVAL
 | '.' -> yield OUTVAL
 | ',' -> yield INPVAL
 | '[' -> yield WHILE
 | ']' -> yield WEND 
 | _ -> yield NOOP
 }
 |> Seq.toList
module Brainfart =
 open System.Text
 open System
 // NOTE: have a function for these one-liners for consistency? or inline it?
 let IncPtr (state : State) : State =
 {state with Pointer = state.Pointer + 1}
 let DecPtr (state : State) : State =
 {state with Pointer = state.Pointer - 1}
 let IncVal (state : State) : Unit =
 // NOTE: I'm mutating the state of the incoming variable, then this has the appropriate optimization effect, 
 // but my state is only mostly immutable. is there some other way to structure this that is more 
 // natural in F#?
 state.Memory.[state.Pointer] <- (state.Memory.[state.Pointer] + 1uy)
 let DecVal (state : State) : Unit =
 state.Memory.[state.Pointer] <- (state.Memory.[state.Pointer] - 1uy)
 let OutVal (state : State) : Unit = 
 state.Memory.[state.Pointer] 
 |> Array.singleton 
 |> ASCIIEncoding.ASCII.GetString
 |> printf "%s" 
 |> ignore
 let InpVal (state : State) : State = 
 // NOTE: this is a two step operation. any way to make this nicer?
 let o, i = Stack.Pop state.Input
 match o with
 | Some v -> state.Memory.[state.Pointer] <- v
 | None -> state.Memory.[state.Pointer] <- 0uy 
 {state with Input = i} 
 let While (state : State) : State = 
 {state with Stack = state.Index :: state.Stack}
 let Wend (state : State) : State = 
 if state.Memory.[state.Pointer] <> 0uy then 
 {state with Index = List.head state.Stack} 
 else 
 // NOTE: this is a two step operation. any way to make this nicer?
 let o, s = Stack.Pop state.Stack
 match o with
 // NOTE: i don't use this variable. any way to not declare it?
 | Some v -> {state with Stack = s} 
 | None -> state
 let IncIndex (state : State) : State =
 {state with Index = state.Index + 1}
 let RunInstruction (instruction : Instruction) (state : State) : State =
 match instruction with
 | INCPTR -> IncPtr state
 | DECPTR -> DecPtr state 
 | INCVAL -> Railway.Tee IncVal state
 | DECVAL -> Railway.Tee DecVal state 
 | OUTVAL -> Railway.Tee OutVal state 
 | INPVAL -> InpVal state 
 | WHILE -> While state
 | WEND -> Wend state
 | NOOP -> state
 // NOTE: most of the operations above make a new copy of the state and IncIndex
 // doubles-down on that. i had the increments inline for each operation
 // but felt like it was too much copied code. any thoughts? it is nicer
 // to look at this way.
 |> IncIndex 
 let rec RunInstructions (code : Instruction list) (state : State) : State =
 if state.Index = code.Length then
 state
 else
 state |> RunInstruction code.[state.Index] |> RunInstructions code
 // NOTE: i get very self-conscious about explicitly type signatures, but they
 // are very useful to me. they help with intellisense and at-a-glance
 // "what does this method do" stuff. should i work on getting away from
 // doing this?
 let Run (input : string) (code : Instruction list) : State =
 let f = printfn "" 
 State.Create input 
 |> RunInstructions code 
 // NOTE: any nicer way to do this? 
 |> Railway.Tee (fun s -> printfn "") 

Here's the fsx script I use for the "Hello World" program.

Source.Compile "++++++++++[>+++++++>++++++++++>+++>++++<<<<-]>++.>+.+++++++..+++.>>++++.<++.<++++++++.--------.+++.------.--------.>+." 
|> Brainfart.Run ""

EDIT

I recognize have a do/while in my code vs a while/do and that i need to change it. I also have a bunch of other questions in the code that are of interest to me. I'd appreciate any insight anyone can give.

msanford
3911 gold badge7 silver badges20 bronze badges
asked Oct 26, 2017 at 17:32
\$\endgroup\$
0

1 Answer 1

4
\$\begingroup\$
let While (state : State) : State = 
 {state with Stack = state.Index :: state.Stack}
let Wend (state : State) : State = 
 if state.Memory.[state.Pointer] <> 0uy then 
 {state with Index = List.head state.Stack} 
 else 
 // NOTE: this is a two step operation. any way to make this nicer?
 let o, s = Stack.Pop state.Stack
 match o with
 // NOTE: i don't use this variable. any way to not declare it?
 | Some v -> {state with Stack = s} 
 | None -> state

The While handler has no conditional logic, which is weird... and wrong. Instead, the conditional logic is in Wend. You have essentially committed the same mistake as in this implementation.

So, Brainfart is indeed an appropriate name for your module. =)

answered Oct 26, 2017 at 19:29
\$\endgroup\$
1
  • \$\begingroup\$ is this a semantics argument or something deeper? would calling them "Do" and "Until" square it away? will the system behave differently if i make the changes you suggest? \$\endgroup\$ Commented Oct 26, 2017 at 19:57

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.