1
\$\begingroup\$

I have this type:

type Future<'a> () =
 let mutable _value: 'a option = None
 member x.Resolve value =
 if _value.IsSome then failwith "can only resolve once"
 _value <- Some value
 member x.IsResolved with get () = _value.IsSome
 member x.Value 
 with get () = 
 match _value with 
 | Some v -> v
 | None -> failwith "hasn't resolved yet"

And I need something like this because I have something along the lines of:

let testHttpGet response =
 let urlFuture = Future<string>()
 let get url = 
 urlFuture.Resolve url
 response
 (get, urlFuture)

Call that function and set the return value of the returned function

let get, urlFuture = testHttpGet "resp"

Call the returned func, thus resolving the Future

let response = get "http://test"

In the end we have the result of the function

response |> should equal "resp"

And we have the value with which the func was called

urlFuture.IsResolved |> should be True
urlFuture.Value |> should equal "http://test"

To me this looks like a sound approach but I also have a feeling that something like it must exist in f# allready

Here's where I use them.

Here is a fiddle.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jan 16, 2015 at 14:54
\$\endgroup\$
3
  • \$\begingroup\$ The provided testHttpGet function doesn't compile. Can you update the code so that we can see what you mean to do? \$\endgroup\$ Commented Jan 16, 2015 at 16:23
  • \$\begingroup\$ Oops yes offcourse! \$\endgroup\$ Commented Jan 16, 2015 at 16:36
  • \$\begingroup\$ @MarkSeemann edited and added a fiddle \$\endgroup\$ Commented Jan 16, 2015 at 16:51

2 Answers 2

3
\$\begingroup\$

This looks pretty close to a Clojure Promise. On .NET, the similar type is (very Microsoftish) called TaskCompletionSource<TResult>.

However, you should consider if F# Lazy Computations isn't what you need.

answered Jan 16, 2015 at 18:18
\$\endgroup\$
1
  • \$\begingroup\$ Lazy computations seem like the way to go :) i'll have a look at it! Thanks. \$\endgroup\$ Commented Jan 16, 2015 at 18:42
3
\$\begingroup\$

Forget about the mutable for a moment. To me, using exceptions and methods called IsXXX are a bit of code smell in a functional language.

Here's how I personally would rewrite the Future class:

  • Remove IsResolved completely.
  • Change Value to return an Option. You can get the value and whether it is resolved in one step.
  • Change Resolve to not throw an exception if already resolved, but instead return a boolean. This also means you can avoid having to have a hard-coded error string -- let the caller decide the error message instead.

.

type Future<'a> () =
 let mutable _value: 'a option = None
 /// Set the value to resolve
 /// If already resolved, return false, else true.
 member x.Resolve value =
 match _value with
 | Some _ -> 
 false
 | None -> 
 _value <- Some value
 true
 /// Return the value to resolve
 /// If not resolved, return None, else Some value
 member x.Value with get () = _value 

Now you can create the testHttpGet function as before.

But because Resolve now returns a bool, you have to explicitly handle it. For now, I've used ignore, but you can see that the potential for a bug has been made more visible to the caller.

let testHttpGet response =
 let urlFuture = Future<string>()
 let get url = 
 urlFuture.Resolve url |> ignore // code smell!
 response
 (get, urlFuture)

Now use the testHttpGet function:

let get, urlFuture = testHttpGet "resp"
let response = get "http://test"

Finally, in the test assertions, the IsResolved method is not needed, as the Value method can do double duty.

//urlFuture.IsResolved |> should be True // not needed
urlFuture.Value |> should equal (Some "http://test")

Going back to the question of a mutable now, I'm not sure why your design calls for separating a task from its result?

Why not combine them (that is, use Lazy as suggested by Mark), or use something like Async where you can chain a series of "callbacks" together with no need to test whether they have resolved or not.

answered Jan 18, 2015 at 12:02
\$\endgroup\$
2
  • \$\begingroup\$ Wow! Scott Wlaschin! :). Thanks. Why are funcs or properties called IsXXX bad practice in functional languages? \$\endgroup\$ Commented Jan 18, 2015 at 12:59
  • 1
    \$\begingroup\$ If the value exists you're going to do something, almost always with that value. So why have two methods when you can just use an option to capture both? It's the lack of options in C# and Java that has led to that IsXXX pattern. See also existentialtype.wordpress.com/2011/03/15/boolean-blindness (a bit technical, but his points are good) \$\endgroup\$ Commented Jan 18, 2015 at 14:22

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.