Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Click and DoubleTap actions #13

Open
wants to merge 2 commits into
base: rr-example-of-new-op
Choose a base branch
from

Conversation

raulraja
Copy link
Collaborator

@raulraja raulraja commented Jul 5, 2017

Addresses #10 #4 #3

Added two new action simply using extension functions. this actions click() and doubleTap can operate both over single UiObject or lists of UiObject actions. Note that regardless of this language and the fluid DSL style nothing is executed until you run and pass it a UiDevice

fun clickExamples(): DSLAction<List<Boolean>> = listOf(
            findObject(UiSelector().description("1")),
            findObject(UiSelector().description("2"))).click()

fun doubleTapExamples(): DSLAction<List<Boolean>> = listOf(
            findObject(UiSelector().description("1")),
            findObject(UiSelector().description("2"))).doubleTap()

fun singleClick(): DSLAction<Boolean> = 
            findObject(UiSelector().description("1")).click()

Since we are operating in a monadic context and building the instructions of our program we have to model operations in terms of flatMap which represents the next step the program will evaluate. Once the previous one is finished.

Since doubleTap means click + click only if the first click has been successful we implement it like this:

fun DSLAction<UiObject>.doubleTap(): DSLAction<Boolean> =
        click().flatMap { firstClick ->
            if (firstClick) click()
            else GestureDSL.pure(false)
        }

@raulraja raulraja changed the base branch from master to rr-example-of-new-op July 5, 2017 00:13
@Guardiola31337
Copy link
Owner

Guardiola31337 commented Jul 15, 2017

@raulraja
In #12 you mentioned that in order to add a new operation you normally follow below steps:

In essence Free monads boilerplate is frequently:

  1. Define an ADT representing your free operations with no semantics as to how they are implemented but capturing all args that you need in order to evaluate them
  2. Lift those operations to the context of Free via liftF using smart constructors
  3. Write an interpreter that can prove you are covering all cases to go from your GestureDSL to any M[_] in this case the SafeInterpreter goes to any type that can support MonadError[M, Throwable] and Try is one of them. That is why in the run methods in our DSL we are using Try.
  4. If you observe how run is implemented you'll see that it calls foldMap on the final program:
fun <A> Free<GestureDSL.F, A>.run(device: UiDevice): Try<A> =
       this.foldMap(SafeInterpreter(Try, device), Try).ev()

Why not in the case of click and doubleTap? How do we decide which approach to chose?


  • doubleTap needs to set actionAcknowledgmentTimeout up before implementing the double click and restore it back after so that it behaves properly. How do we add/implement this?
  • How should a gesture test (i.e. DoubleTapTest) look like now using GestureDSL? (We're now in a good position to address How do we test GestureDSL? #7)

@raulraja
Copy link
Collaborator Author

Normally you add to the ADT class hierarchy those operations that you consider primitive operations.
If you can create an operation using existing operations you may choose to just add the functions using the others like we did in this example.

Since you need to setup resources in this case you may just:

  1. Change click and doubleTap to be operations in the ADT.
  2. When you implement them in the interpreter you may first set actionAcknowledgmentTimeout, execute the action then call restore all in the interpreter implementation for Click and DoubleTap on each one of its implementation; assuming that state is something you are changing in the UIDevice which you have access to in the interpreter.

Alternatively you could keep them as is and call the operation withDevice as part of the composition and perform those effects: In pseudo code:

fun doubleTap() = GestureDSL.binding {
   val device = ! withDevice
   ... setup actionAcknowledgmentTimeout
   val result = ! doDoubleTapAction
   ... tear down actionAcknowledgmentTimeout
   yields (result)
}

@raulraja
Copy link
Collaborator Author

If you want to give it a shot at compiling whatever you decide from the proposal above I can once you have the implementation show you how to test the final implementation. The test would look different dependeding if you decide to implement those actions as part of the DSL ADT vs a monadic composition in a function like in the example above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants