1

Want to convert some C# code for RX to F# code. The following C# code works well:

  var seqNum = Observable.Range(1, 5);
  var seqString = from n in seqNum
                  select new string('*', (int)n);
  seqString.Subscribe(str => { Console.WriteLine(str); });
  Console.ReadKey();

The following is my code in F#:

#light
open System
open System.Collections.Generic
open System.Linq
open System.Reactive
open System.Reactive.Linq
open System.Reactive.Subjects
open System.Threading
open System.IO

let seqNum = Observable.Range(1, 5)
let seqString = from n in seqNum
                select new string('*', (int)n)
Console.ReadLine() |> ignore

But I got the following compiler error: Error: Unexpected keyword 'new' in implementation file

If I deleted the new keyword, I got another error: Error: Successive arguments should be separated by spaces or tupled, and arguments involving function or method applications should be parenthesized

The "new" keyword are totally different in C# and F#. Please show me how to do the same job in F#. Thanks,

John John
  • 377
  • 1
  • 11

3 Answers3

3

In C# string is the shortcut to System.String class. However, in F# string is a function which has obj as its input and returns a string which is overriden in obj.ToString():

let s = string('*', 3);; // tuple to string
// val s : string = "(*, 3)"

What you really want is creating a string by repeating '*' three times:

let s = new String('*', 3)
// val a : String = "***"

To be clear, from ... in ... select ... is C# LINQ syntax which is invalid in F#. Therefore, using computation expression instead:

let seqNum = seq {1..5}
let seqString = seq { for n in seqNum -> new String('*', n) }

To get some ideas of creating/using computation expression for Reactive Extension, take a look at the question and its answers at How do I change the Rx Builder implementation to fix the stack overflow exception?

Community
  • 1
  • 1
pad
  • 41,040
  • 7
  • 92
  • 166
  • Hi, Pad: Thank you, your code works. However, from the original C# code, I don't know how to subscribe to the Observable. Please show me how I can subscribe to it. – John John Jan 08 '12 at 21:47
  • @John: you were too fast to accept the pad's answer. See below my working F# port of your original C# snippet. Though you cannot use LINQ in F# C# way, nevertheless you can use it thru "dot" notation. – Gene Belitski Jan 08 '12 at 21:52
  • Something like `seqString.Subscribe(fun s -> Console.WriteLine(s))` would work. – pad Jan 08 '12 at 21:54
2

Instead of using the String constructor use the String.replicate method.

String.replicate n "*"

There is no direct equivalent for String(char, int) but String.replicate: int -> string -> string is roughly the equivalent with string instead of char

F# version for that code

[1 .. 5]
|> Seq.map (fun i -> String.replicate i "*")
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Hi: JaredPar: I changed my code to: let seqString = from n in seqNum select (String.replicate n "*") But I got even more terrible compiler error: Error: The value or constructor 'from' is not defined The F# compiler did NOT even recogize keyword "from" for LINQ. Tell me what to do. Thanks! – John John Jan 08 '12 at 21:19
  • @JohnJohn I thought you were using the preview version that they added LINQ to (pretty sure it's added there). The RTM version doesn't have any support for LINQ. I'll add the equivalent F# version for that particular code. – JaredPar Jan 08 '12 at 21:28
  • Hi, JaredPar: I have changed my code to look like this: let xx = [1 .. 5] |> Seq.map (fun i -> String.replicate i "*") |> (fun x -> x.ToObservable()) |> (fun y -> y.Subscribe(printfn "%s")) – John John Jan 09 '12 at 18:17
  • Hi, JaredPar: your code is better, but it didn't do what the original code did: to subscribe from an observable. I added some code that do the job, let me know what you think! Thanks for your help! – John John Jan 09 '12 at 18:19
  • @JohnJohn I'm less familiar with `Observable` but your code does look correct to me. – JaredPar Jan 09 '12 at 18:52
1

Here you go:

open System
open System.IO
open System.Reactive
open System.Reactive.Linq

let seqString = Observable.Range(1,5).Select(fun x -> String.replicate x "*")
using (seqString.Subscribe (fun x -> printfn "%s" x))
    (fun _ -> Console.ReadLine() ) |> ignore

EDIT: As Paul suggested below two last lines can be replaced by simple

seqString.Subscribe (printfn "%s") |> ignore

However, if we want to gracefully unsubscribe from our subscription, but get rid of using in lieu of newer use syntax we may replace last two lines by the following three

do
    use subscription = seqString.Subscribe(printfn "%s")
    Console.ReadLine() |> ignore
Gene Belitski
  • 10,270
  • 1
  • 34
  • 54
  • You don't need to (and in fact, shouldn't) Dispose the subscription here – Ana Betts Jan 08 '12 at 21:58
  • @PaulBetts: (a) `Console.ReadLine()` within `using` block guarantees that disposal will take place only **after ** pressing `Enter` on keyboard as original C# code suggests; and (b) more importantly, `Observable.Range(1,5)` is equivalent to `Observable.Range(1,5,Scheduler.CurrentThread)` making the code effectively single-threaded, so disposing subscription here is absolutely safe, why shouldn't I? – Gene Belitski Jan 09 '12 at 06:09
  • Hi, Gene Belitski: Thank you very much, your code works. However, it is difficult for me to know when to use "use" and when to use "using", I am thinking only C# uses "using", not F#! – John John Jan 09 '12 at 18:21
  • 1
    Because the semantics of IObservable.Subscribe are that you Dispose it only if you want to terminate the subscription early. In this case, you don't *want* to dispose it early, and the only way you get away with it is that the source is scheduled in-context – Ana Betts Jan 09 '12 at 18:49
  • @JohnJohn: You may want to look [here](http://stackoverflow.com/questions/4973795/f-keyword-use-and-using) for explanations of difference between `use` and `using`. But you also can safely stick to `use`, which is a syntactic sugar form of binding that ensures automagical call of `Dispose()` when `IDisposable` value goes out of scope – Gene Belitski Jan 09 '12 at 21:40