开发者

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开发者_如何学JAVA a sexy one-liner for this:

let aliases = (Seq.initInfinite (sprintf "t%d")).GetEnumerator()

let getNextAlias() = 
    aliases.MoveNext() |> ignore
    aliases.Current


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.


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


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜