Rewriting a simple C # nested class
What would be an elegant way to implement the functionality of this nested class in F #?
private class Aliaser {
private int _count;
internal Aliaser() { }
internal string GetNextAlias() {
return "t" + (_count++).ToString();
}
}
This was my first try, but it looks like there should be a sexy one-liner for this:
let aliases = (Seq.initInfinite (sprintf "t%d")).GetEnumerator()
let getNextAlias() =
aliases.MoveNext() |> ignore
aliases.Current
a source to share
The usual way of writing is to create a function with the local state committed to the closure:
let getNextAlias =
let count = ref 0
(fun () ->
count := !count + 1;
sprintf "t%d" (!count))
The type getNextAlias
is simple unit -> string
, and when you call it again it returns the strings "t1", "t2", ... It depends on the mutable state, but the mutable state is hidden from the user.
Regarding whether you can do it statelessly - the simple answer is NO, because when you call a purely functional function with the same parameter twice, it should return the same result. Thus, you need to write something with the following structure:
let alias, state1 = getNextAlias state0
printf "first alias %s" alias
let alias, state2 = getNextAlias state1
printf "second alias %s" alias
// ...
As you can see, you need to keep some state and keep it throughout your code. In F #, the standard way to deal with this is to use mutable state. In Haskell, you can use the state monad, which allows you to hide the transfer of state. Using the implementation from this question , you can write something like:
let getNextAlias = state {
let! n = getState
do! setState (n + 1)
return sprintf "t%d" n }
let program =
state {
let! alias1 = getNextAlias()
let! alias2 = getNextAlias()
// ...
}
execute progam 0 // execute with initial state
This is very similar to other computations, such as lazy
or seq
, in fact - the computations in a block state { .. }
have some state and you can perform them by providing an initial state value. However, unless you have a compelling reason to demand a purely functional solution, I would prefer the first version for practical F # programming.
a source to share
Here's a quick and dirty translation
type Aliaser () =
let mutable _count = 0
member x.GetNextAlias() =
let value = _count.ToString()
_count <- _count + 1
"t" + value
A more functional stateless approach is to use continuations.
let createAliaser callWithValue =
let rec inner count =
let value = "t" + (count.ToString())
callWithValue value (fun () -> inner (count + 1))
inner 1
This is a declaration that will call a function callWithValue
for both the value and the function that will execute to iterate over the next value.
And here is an example of use
let main () =
let inner value (next : unit -> unit )=
printfn "Value: %s" value
let input = System.Console.ReadLine()
if input <> "quit" then next()
createAliaser inner
main()
a source to share