How can I repeat calling a function until it returns None?

426 Views Asked by At

Suppose I have 2 functions

Func<Id, Option<Employee>> FindEmployee

It returns an employee if the Id is found, otherwise None;

Func<Employee, Option<Entry>> PromptPassword

It will open a dialog asking for password, if OK button is hit, it will return the user entry; if cancel is hit, it will return None

I would like to have an elegant way to composite these 2 functions, basically I want to do:

FindEmployee.Map(emp => 
  {
     while (true)
     {
         var result = PromptPassword (emp);
         if (result.IsNone)
         {
             return false;
         }

         bool matched = result.Where(a => a.EntryContent == emp.Password)
                              .Some(r => true)
                              .None(false);
         if (matched)
             return true;
      }
  });

The end result is an Option You see, I want to keep prompting for password until the user enter it correctly. But using a while loop inside a Map is so ugly.

It must have a better way to write this. can anyone give a hint?

Thanks

3

There are 3 best solutions below

0
dumetrulo On BEST ANSWER

In F# I would probably express your algorithm like this:

findEmployee |> Option.map (fun emp ->
    let rec loop () =
        match PromptPassword emp with
        | None -> false
        | Some a -> a.EntryContent = emp.Password || loop ()
    loop ())

The equivalent in C# would probably look as follows:

FindEmployee.Map(emp =>
    {
        do {
            var result = PromptPassword(emp);
            if (result.IsNone) return false;
        } while (result.Where(a => a.EntryContent == emp.Password).IsNone);
        return true;
    });
2
Albin Philip On

You could make use of the goto statement right? and then get rid of the while loop...

3
caeus On

define a function like this

bool Insist(Employee:emp){
  var result = PromptPassword (emp);
  if (result.IsNone)
  {
     return false;
  }

  bool matched = result.Where(a => a.EntryContent == emp.Password)
                              .Some(r => true)
                              .None(false);
  if(matched)
  {
     return true;
  }else
  {
     return Insist(emp);
  }

}

Then you can just go and

FindEmployee(id).Map(emp => 
  {
     return Insist(emp);
  });