1

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

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
sowen
  • 1,090
  • 9
  • 28
  • 2
    Out of curiosity, which Option is this? Is it from F# or some other library? – Wyck Feb 07 '18 at 05:31
  • 1
    Also, why does PromptPassword receive an Employee? Why, if the password verification happens later in the line starting `bool matches = ...`. And a `while(true){...}` will keep iterating forever. I mean, your snippet of code is wrong, reglardless of its elegance. It just won't work. You need to edit your question so that at least your snippet of code makes sense. – caeus Feb 07 '18 at 09:26
  • @caeus Possibly some value in `emp` is used in the prompt dialog. And a `while` loop will not iterate forever if the code `return`s. I see no reason to believe that OP's code doesn't work. – JLRishe Feb 07 '18 at 09:57
  • @JLRishe, you're right about the return. It will make it end. What's the type of "emp"? Employee or Option? – caeus Feb 07 '18 at 10:03
  • @caeus Given that OP is accessing `emp.Password`, I'd imagine it's `Employee`. – JLRishe Feb 07 '18 at 10:20
  • So then what is the type signature of that `.Map` method? It's super confusing. Looks like it's a method to compose functions, but if that were the case, the type of emp, should be Option – caeus Feb 07 '18 at 10:22
  • @caeus Monadic map methods provide a way to operate on the monad's underlying type. So I'd imagine that `Option.Map()` takes a `Func` as its argument and returns an `Option` (in this case, `T` would be `bool`). – JLRishe Feb 07 '18 at 10:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164684/discussion-between-caeus-and-jlrishe). – caeus Feb 07 '18 at 10:30
  • @caeus C# has Map, I am using language-ext, which is an extension on top of Linq, so it has Map, Bind, Option, Either, etc. – sowen Feb 07 '18 at 14:56

3 Answers3

1

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);
  });
caeus
  • 3,084
  • 1
  • 22
  • 36
  • I reckon you could change the second part to `FindEmployee.Map(Insist)`. Not sure if this is much of an improvement though. I think OP is looking for a pre-established pattern for doing this kind of thing in FP/Rx. – JLRishe Feb 07 '18 at 10:31
  • Well, I'm not really sure if that's possible in C#. As he's asking for FP advice, nothing more FP that good ol' recursion. Haha – caeus Feb 07 '18 at 10:33
  • I actually do feel I should just write an additional method using the pass-in function. thanks for the reply – sowen Feb 07 '18 at 14:57
1

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;
    });
dumetrulo
  • 1,993
  • 9
  • 11
-5

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