Daniel
Daniel

Reputation: 47904

Rewriting 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 attempt, but it feels 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

Upvotes: 2

Views: 941

Answers (3)

Huusom
Huusom

Reputation: 5912

I would use Seq.unfold : (('a -> ('b * 'a) option) -> 'a -> seq<'b>) to generate the aliases.

Implemented as:

let alias = 
    Seq.unfold (fun count -> Some(sprintf "t%i" count, count+1)) 0

Upvotes: 0

Tomas Petricek
Tomas Petricek

Reputation: 243051

The usual way of writing is to create a function with local state captured in a closure:

let getNextAlias = 
  let count = ref 0
  (fun () -> 
     count := !count + 1; 
     sprintf "t%d" (!count))

The type of getNextAlias is simply unit -> string and when you call it repeatedly, it returns strings "t1", "t2", ... This relies on mutable state, but the mutable state is hidden from the user.

Regarding whether you can do this without mutable state - the simple answer is NO, because when you call a purely functional function with the same parameter twice, it must return the same result. Thus, you'd have 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'd need to keep some state and maintain it through the whole code. In F#, the standard way of dealing with this is to use mutable state. In Haskell, you could use State monad, which allows you to hide the passing of the state. Using the implementation from this question, you could 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 quite similar to other computations such as lazy or seq, actually - computations in the state { .. } block have some state and you can execute them by providing initial value of the state. However, unless you have good reasons for requiring purely functional solution, I'd prefer the first version for practical F# programming.

Upvotes: 7

JaredPar
JaredPar

Reputation: 754645

Here is the 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 approach without state 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 which will call the function callWithValue with both the value and the function to execute to repeat with the next value.

And here's an example using it

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()

Upvotes: 2

Related Questions