Elmish.WPF icon indicating copy to clipboard operation
Elmish.WPF copied to clipboard

Add vm type information to `BindingData` and `VmBinding`

Open marner2 opened this issue 3 years ago • 25 comments
trafficstars

Waiting on #505 and #504

Compare without 505

This Pull Request adds another type parameter to BindingData which allows it to carry the actual view model property type statically through the builder.

From a conversation with @TysonMN:

When used in the context of "dot" notation in the XAML project, you will be able to get compile-time help for things like ViewModelProp.SubModel.Prop. If those were constrained to obj, you wouldn't get compile-time checking of those types. Also XAML will warn you about type issues (number vs string, etc)

Note that the current usage of Binding<'model,'msg> constrains the new type parameter to be obj for every construction of it (this is the entire public interface in Binding.fs). This is intentional, as all of those bindings are by necessity constrained to obj in order to work in the various SubModel and SubModelSeq bindings. The advantage mentioned above will not be realized until after a static helper is created that can turn a binding into a statically-typed getter or setter.

marner2 avatar Jun 02 '22 20:06 marner2

I merged the previous PR using FastHub with merge type "rebase". I see this merged a commit with a different hash.

The other options were "merge" and "squash". I think "merge" creates a merge commit. I will try "squash" with the next PR.

Neither FastHub nor GitHub's app provide me the ability to update the branch. In GitHub's app though, there is a card saying that there are merge conflicts. I will check shortly if I can fix this on the GitHub website on my work laptop.

TysonMN avatar Jun 04 '22 11:06 TysonMN

I don't see a way to drop the previous commit via the GitHub website. Can you do that and push the resulting branch?

TysonMN avatar Jun 04 '22 14:06 TysonMN

I think you accidentally added a second commit to the branch in this PR

TysonMN avatar Jun 07 '22 03:06 TysonMN

@TysonMN no I flipped the order. Merge #471 first now

marner2 avatar Jun 07 '22 03:06 marner2

Nvm. I now see you did that on purpose.

TysonMN avatar Jun 07 '22 03:06 TysonMN

Why always the ValueOption.map box >> ValueOption.toObj >> unbox sequence? Does type safety interfere with ValueOption.toObj somehow?

LyndonGingerich avatar Jun 13 '22 13:06 LyndonGingerich

Converting to draft until #472 is merged in.

marner2 avatar Jun 14 '22 02:06 marner2

@the-not-mad-psychologist

Why always the ValueOption.map box >> ValueOption.toObj >> unbox sequence? Does type safety interfere with ValueOption.toObj somehow?

What it does is handle both cases where 'a is nullable and isn't nullable. Boxing the inner value and then unboxing it like this makes a nullable type just return null, while a non-nullable type will throw an exception in the null case (which is correct behavior).

marner2 avatar Jun 14 '22 16:06 marner2

Converting into draft until #474 is merged in. Also added tests (enabled by #474)

marner2 avatar Jun 14 '22 17:06 marner2

@TysonMN I had to revert some of the changes from PR 474 that you added because they moved the box functions into BindingData which breaks this PR. That boxing now doesn't happen until createBinding is called. However, it's possible that we could reintroduce those changes just with id instead of box.

marner2 avatar Jun 20 '22 14:06 marner2

Which commit in this PR requires the changes in the first commit?

TysonMN avatar Jun 25 '22 05:06 TysonMN

@TysonMN The second commit (7bce311) moves all of the box and unbox calls into BindingData.mapVm

marner2 avatar Jun 25 '22 16:06 marner2

Can you make a PR before this one that adds ofNull and toNull for ValueOption along with some tests containing examples with data on which they could be called?

TysonMN avatar Jun 28 '22 02:06 TysonMN

@TysonMN added in #495

marner2 avatar Jun 29 '22 01:06 marner2

@TysonMN this test failure isn't real. The check needs to be re-run.

marner2 avatar Jul 01 '22 00:07 marner2

I want to see how toNull and ofNull in ValueOption are used. Specifically, I want to see a use case where toNull returns Error.

Can this happen with both a dynamic view model and a static view model?

Can you create executable projects for each type of view model for which this is possible?

For a dynamic view model, you can edit one of the samples if you want. We don't necessarily need to merge in this code. Best case scenario is that this helps me figure out how to make this error case impossible. If I can't find a way to make it impossible, I might turn this code into tests.

TysonMN avatar Jul 07 '22 12:07 TysonMN

@TysonMN it's currently always impossible in DynamicViewModel because the type of 'vm is always obj, which is always nullable.

As to where it is used, ValueOption.toNull is used in 3 places in Get<'a>.Base(). ValueOption.ofNull is used in one place in Set<'a>.Base(). DynamicViewModel only ever calls Get<obj> and Set<obj>, so there's nothing really to test other than that null values go all the way through.

I can add a test in BindingVmHelpersTests.fs to illustrate the failure mode. Basically you create one using Initialize<'a>() for a nullable type, set it to null, box it to obj (needed for Update()), unbox it to a non-nullable type, then call Get<'a>() on it.

marner2 avatar Jul 07 '22 19:07 marner2

@TysonMN for a minimal example of how this error can be induced, see commit 2d31bad

marner2 avatar Jul 07 '22 19:07 marner2

Quoting https://github.com/elmish/Elmish.WPF/pull/470#issuecomment-1160492351

However, it's possible that we could reintroduce those changes just with id instead of box.

I think I prefer that. Can you make that change?

TysonMN avatar Jul 07 '22 22:07 TysonMN

Quoting https://github.com/elmish/Elmish.WPF/pull/470#issuecomment-1160492351

However, it's possible that we could reintroduce those changes just with id instead of box.

I think I prefer that. Can you make that change?

It's in the first commit already. I liked it a whole lot better.

marner2 avatar Jul 07 '22 22:07 marner2

Can you squash the commit with message

Add some tests for binding vm helpers

into the commit that adds the code being tested?

Can you also squash the commit with message

Apply suggestions from code review

into the various commits where the code being changed was first added/changed?

TysonMN avatar Jul 11 '22 11:07 TysonMN

@TysonMN those squashes should now be accurate.

marner2 avatar Jul 11 '22 19:07 marner2

Yes, always ;)

TysonMN avatar Jul 13 '22 12:07 TysonMN

@TysonMN moved the other comment to the right place. Do you have anything else for this PR?

marner2 avatar Jul 13 '22 15:07 marner2

@TysonMN I put #498 before this one now, since I was rebasing a lot. That one now needs to be merged in first

marner2 avatar Jul 22 '22 17:07 marner2

For each comment that had a pin put in it, can you add a comment on the new PR that is adding that code, link back to the pinned comment here, quote what I asked, and (if you responded here) quote your response?

TysonMN avatar Sep 08 '22 01:09 TysonMN

For each comment that had a pin put in it, can you add a comment on the new PR that is adding that code, link back to the pinned comment here, quote what I asked, and (if you responded here) quote your response?

Done for every comment explicitly referenced as pinned for a future PR.

LyndonGingerich avatar Sep 08 '22 13:09 LyndonGingerich