Search code examples
c#f#translateforward-declaration

C# to F# and Interdependent Functions


I would appreciate it if someone kindly helps me with translating this into F#. Not in the class form of course: 1) If I declare ThreadProc first, ThreadProc is supposed to use thread1 and thread2 which are later defined in the main using ThreadProc in the constructors; and if I declare main first, the initialisations will use a function that is not yet defined. 2) If I declare top-level thread1 and thread2 before defining function ThreadProc (e.g., let thread1 = new Thread(fun () -> ())), then that is the version that ThreadProc will ultimately utilise rather than the one later declared in the main.

using System;
using System.Threading;

public class Example
{
    static Thread thread1, thread2;

    public static void Main()
    {
        thread1 = new Thread(ThreadProc);
        thread1.Name = "Thread1";
        thread1.Start();

        thread2 = new Thread(ThreadProc);
        thread2.Name = "Thread2";
        thread2.Start();   
    }   

    private static void ThreadProc()
    {
        Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
        if (Thread.CurrentThread.Name == "Thread1" && 
            thread2.ThreadState != ThreadState.Unstarted)
                thread2.Join();

        Thread.Sleep(4000);
        Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
        Console.WriteLine("Thread1: {0}", thread1.ThreadState);
        Console.WriteLine("Thread2: {0}\n", thread2.ThreadState);
   }
}

Solution

  • This demonstrates what I was referring to in my comment. It uses ParameterizedThreadStart to pass information to the threads via an F# record.

    You are responsible for making sure that the object passed in is of the same type as is expected within the thread proc. The argument to the thread proc is necessarily of type obj, so the compiler can't check the type for you. But you can create an appropriately typed shadow of args with let args = args :?> Args for convenience within the proc.

    open System
    open System.Threading
    
    type Args = { Thread1: Thread; Thread2: Thread }
    
    let threadProc (args: obj) =
        let args = args :?> Args
    
        printfn "\n\nCurrent Thread: %s" Thread.CurrentThread.Name
    
        if Thread.CurrentThread.Name = "Thread 1" && args.Thread2.ThreadState <> ThreadState.Unstarted then
            args.Thread2.Join ()
    
        Thread.Sleep(4000)
        Console.WriteLine( "\n\nCurrent thread: {0}", Thread.CurrentThread.Name )
        Console.WriteLine("Thread 1: {0}", args.Thread1.ThreadState)
        Console.WriteLine("Thread 2: {0}\n", args.Thread2.ThreadState)
    
    let thread1 = new Thread(ParameterizedThreadStart(threadProc))
    thread1.Name <- "Thread 1"
    
    let thread2 = new Thread(ParameterizedThreadStart(threadProc))
    thread2.Name <- "Thread 2"
    
    let main () =
        let args = { Thread1 = thread1; Thread2 = thread2 }
        thread1.Start(args)
        thread2.Start(args)
        System.Console.ReadKey () |> ignore
    
    do main ()