intuit / intuit/hooks

Imperative Hook APIs

Open
#23 2 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Kotlin
Stars
42
Forks
6
PR merge metrics
No merged PRs in 30d

Description

Been brainstorming a bit about imperative hook APIs:

```kotlin
var someController: SataController? = null
container.hooks.someController.tap("some controller") { sc ->
someController = sc
}
// ...
someController?.doSomething()
```

There is a little bit of overhead here for simply tracking the current value in a variable to use at a later point. It’s not terrible, but it get’s pretty bad pretty quick if you need something more nested:

```kotlin
var update: Update? = null
container.hooks.someController.tap("some controller") { sc ->
sc?.hooks?.nestedController?.tap("nested controller") { nc ->
nc?.hooks?.onUpdate?.tap("update") { _update ->
update = _update
}
}
}
```

With Kotlin, we can do something called property delegation, essentially some syntactic sugar for consolidating logic around custom getters and setters. The idea, is that we can do something like:

```kotlin
val someController by container.hooks.someController()
```

Powered by something like this under the hood:

```kotlin
operator fun Hook1.invoke() = Capture(this)

class Capture(hook: Hook1) {

private var current: T1? = null

init {
hook.tap("capture") { _, value: T1 ->
current = value
}
}

operator fun getValue(thisRef: Any?, property: KProperty<*>): T1 = current
?: throw IllegalStateException("hook not currently active")
}
```

Relatively slim extension for capturing a hooks latest value. However, it falls short when we look at the nested hooks case. Because accessing the value of the captured hook is stateful, we cannot just add another capture:

```kotlin
val someController by container.hooks.someController()
val nestedController by someController.hooks.nestedController()
```

I thought a bit about how much effort it would take to delegate instance methods to the Capture instance itself, but I think that requires too much overhead in how hook instances are defined (would require an interface to delegate to, or some more code gen for duplicating and forwarding instance APIs) and wouldn’t really be user friendly, even if the end API would be somewhat nice:

```kotlin
val nested by container.hooks.someController().hooks.nestedController()
```

Instead, I’m currently looking into a callback model, much like how taps exist now, but would still enable the delegation API:

```kotlin
val nested by container.hooks.someController { hooks.nestedController }
```

And then for additional levels:

```kotlin
val update by container.hooks.someController { hooks.nestedController }.capture { hooks.onUpdate }
```

Thoughts? Really open to anything, as I’ve got some concerns about how this could be a dangerous pitfall for those who don’t understand hooks.. at least with tapping, it’s very clear that you’re responding when you need to. I’m not even sure how many valid use cases there are for imperatively introspecting the latest value of the hook outside the tap. Especially when you take into account different types of hooks, and how many of them are really pipeline/event based, rather than stateful sync hooks.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.