# Tag Variable Hosting (round 3)
So... While trying to implement tag variable hoisting, we ran into some conceptual issues. What happens in these cases?
1. `<my-input-range>`
```marko
<div>${value}</div>
<my-input-range/value/>
```
2. `<dims>`
```marko
<div>
<dims/{ x, y }/>
</div>
<span>
The div is ${x}px wide ${y}px tall
</span>
```
3. `<carousel>` with paddles
```marko
<Paddles/>
<div>
<carousel/Paddles>
</div>
```
```marko
<Paddles>
<carousel/Paddles>
</Paddles>
<my-dialog focusEl=el>
<button/el/>
<log=el/>
<script>
el;
</script>
</>
<const/{ focusEl, content }=addDefaults(input)>
<${content}/>
```
## Overview
Hoisting becomes a problem when we can create cycles. Marko has the additional challenge that due to the nature of our reactive system (compiled & push-based), we can have cases (`<${dynamic}>` being the big one) where a hoisted value shouldn't _need_ to be circular but we compile such that input to a tag is grouped together and it _is_ circular.
So we have 4 classes of usage of variables
1. **No hoist**
```
<let/value/>
<my-tag foo=value/>
```
2. **Non-circular hoist**
The value is used outside the scope it is introduced in, or before it in the source order, but its usage does not intersect with the definition in any way.
```
<if=show>
<let/value/>
</if>
<my-tag foo=value/>
```
```
<my-tag foo=value/>
<let/value/>
```
3. **Component-circular hoist**
These _could_ be dependency circular, but they could also not be. Depends on the implementation of `<my-tag>`.
```
<my-tag foo=value>
<let/value/>
</my-tag>
```
```
<my-tag/value foo=value>
```
4. **Dependency-circular hoist**
```
<const/foo=value/>
<let/value=foo/>
```
In the case of a circular hoist (definitely **4**, probably **3** unless we make some significant changes to the reactive system - switch to pull-based), we _have_ to have some rules in place to prevent the cycle.
There is also the question of where we apply these rule.
- Only to circular (3/4)
- or to all hoists (2/3/4)?
## Option 0: No Circular Hoisting
A hoisted variable (or values derived from it) cannot be passed to a tag that wraps the variable.
Works:
```
<my-dialog focus-el=el/>
<some-component>
<button/el/>
</some-component>
```
Not Allowed:
```
<my-dialog focus-el=el>
<some-component>
<button/el/>
</some-component>
</>
```
### Pros:
### Cons:
## Option 1: Functions only
### Restrictions
- If you return a value and render content, the returned value must be a function
- A tag variable that's hoisted
- Must be a function
- Can't be called/invoked during render phase
If a `.marko` template renders any DOM nodes, it is only allowed to return functions. That means that `<my-input-range>` with a return is not possible, and instead the way to accomplish the same goal is with something like
```marko
<let/val=0.5/>
<div>${value}</div>
<my-input-range:=val/>
```
### Pros:
- We can allow double renders at some point in the future
### Cons:
- We _need_ element refs to be functions
- The `<dims>` example can't ever be reactive
## Option 2: Objects via Proxies
### Restrictions
- If you return a value and render content, the returned value must be an object
- A tag variable that's hoisted
- Must be an object
- Can't be destructured or have properties accessed during render phase
- Can't be called/invoked during render phase
- Properties of native element refs can't be read during the render phase
### Pros
- We don't need element refs to be functions?
- We can allow double renders at some point in the future
### Cons
- Proxies can't be passed to DOM apis, which is a problem for native element refs
- Devs will probably see proxies all over the place where they don't expect them
- Proxies are slow
## Option 3: Double Render
The hoisted value is initially undefined (but would be defined before the effect phase?). It then re-executes with the actual value.
### Pros:
- No usage limitations
- Michael's excited about it so he might do it
### Cons:
- How do we double render on the server? Ideally won't send additional code to the browser if
- Requires null checks on all hoisted values
- Extra work being done
- Requires a rethink of the server runtime (probably)
- Do we ever have to triple render? Quadruple?
- If a double render happens to redefine the property it will cause an infinite loop. This may be impossible to avoid in places where expressions are grouped together like the dynamic tag.
## Addon 1: Explicit Hoisting
Many solutions have differing behavior of hoisted values. Having explicitly hoisted values may make it more clear where this unique behavior is present.
Exposgt a global `$hoist` function that must wrap any hoisted value.
```marko
<some-child>
<const/value=123/>
</some-child>
<script>
$hoist(value) // 123
</script>
```
## Addon 2: Explicit Iteration
We've discussed making hoisted, non-primitive values iterable so if a variable is rendered multiple times you can access all values.
However this doesn't support primitive values and may benefit from explicit syntax.
Expose a global `$hoistAll` function that finds all tag variables with a given name and returns a list
```marko
<for|i| until=5>
<const/value=i/>
</for>
<script>
$hoistAll(value) // [0, 1, 2, 3, 4]
</script>
```