{"action":"created","issue":{"url":"https://api.github.com/repos/dackerman/reflex-jsx/issues/2","repository_url":"https://api.github.com/repos/dackerman/reflex-jsx","labels_url":"https://api.github.com/repos/dackerman/reflex-jsx/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/dackerman/reflex-jsx/issues/2/comments","events_url":"https://api.github.com/repos/dackerman/reflex-jsx/issues/2/events","html_url":"https://github.com/dackerman/reflex-jsx/issues/2","id":186251728,"number":2,"title":"Questions about reflex-jsx limitations","user":{"login":"meditans","id":4641445,"avatar_url":"https://avatars.githubusercontent.com/u/4641445?v=3","gravatar_id":"","url":"https://api.github.com/users/meditans","html_url":"https://github.com/meditans","followers_url":"https://api.github.com/users/meditans/followers","following_url":"https://api.github.com/users/meditans/following{/other_user}","gists_url":"https://api.github.com/users/meditans/gists{/gist_id}","starred_url":"https://api.github.com/users/meditans/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/meditans/subscriptions","organizations_url":"https://api.github.com/users/meditans/orgs","repos_url":"https://api.github.com/users/meditans/repos","events_url":"https://api.github.com/users/meditans/events{/privacy}","received_events_url":"https://api.github.com/users/meditans/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":12,"created_at":"2016-10-31T10:33:55Z","updated_at":"2016-11-13T11:30:11Z","closed_at":null,"body":"Hi @dackerman, first of all thanks for this library! I was surprised on the ease of use, once integrated in my `reflex` project. Hi have a question on the limitation you talked about in the `readme`, though:\r\n\r\n> The only thing to remember is that you can't currently get values out of the nodes you insert, so for more complicated data flows, you might not be able to use a jsx block at all. In that case, it is probably easier to use functions anyway.\r\n\r\nIf I understand correctly, this is only a limitation of the library in its current form, right? What should be done to bind dynamic values out of the node we insert?"},"comment":{"url":"https://api.github.com/repos/dackerman/reflex-jsx/issues/comments/260180463","html_url":"https://github.com/dackerman/reflex-jsx/issues/2#issuecomment-260180463","issue_url":"https://api.github.com/repos/dackerman/reflex-jsx/issues/2","id":260180463,"user":{"login":"nmattia","id":6930756,"avatar_url":"https://avatars.githubusercontent.com/u/6930756?v=3","gravatar_id":"","url":"https://api.github.com/users/nmattia","html_url":"https://github.com/nmattia","followers_url":"https://api.github.com/users/nmattia/followers","following_url":"https://api.github.com/users/nmattia/following{/other_user}","gists_url":"https://api.github.com/users/nmattia/gists{/gist_id}","starred_url":"https://api.github.com/users/nmattia/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nmattia/subscriptions","organizations_url":"https://api.github.com/users/nmattia/orgs","repos_url":"https://api.github.com/users/nmattia/repos","events_url":"https://api.github.com/users/nmattia/events{/privacy}","received_events_url":"https://api.github.com/users/nmattia/received_events","type":"User","site_admin":false},"created_at":"2016-11-13T11:30:11Z","updated_at":"2016-11-13T11:30:11Z","body":"Here are some thoughts on the design.\r\n\r\n## Tuple based (HACK)\r\n\r\n\r\nAt some point I needed a solution quickly and didn't have too much time to\r\nspend on it. I simply returned a tuple of the value of the current call and the\r\n\"rest\":\r\n\r\n``` html\r\n<div></div>\r\n<p>Hello</p>\r\n\r\n```\r\n\r\nwill be turned into\r\n\r\n``` haskell\r\n(,) <$> (el \"div\" $ return()) <*> ((,) <$> (el \"p\" $ text \"Hello\") <*> ())\r\n```\r\n\r\nThe branch is [here](https://github.com/dackerman/reflex-jsx/compare/master...nmattia:master).\r\n\r\nI used `(,)` to carry the return type. However for nodes with `n` children we\r\ncould return an `n`-tuple, which would make the matter a bit nicer. Also we can\r\nuse lens to get values out, once we know *where* that value actually is.  This\r\nhighlights the biggest limitation: we can't really name the values. And because\r\nof that we depend on the structure of the returned object, which will change\r\nwhenever the quasiquote changes. Also the return type is polluted with a lot of\r\nvalues that we don't need.\r\n\r\n* **Doesn't introduce too much new syntax**: doesn't introduce any new syntax.\r\n  However the pattern-matching/value extraction can get very verbose.\r\n\r\n* **Doesn't require extra type signatures or too much boilerplate to make\r\n  work**: Nothing special to do, except for getting that darn value out of the\r\n  tuple.\r\n\r\n* **Easy to reason about - i.e. not too \"magical\"**: Not magical at all, pretty\r\n  caveman-ish.\r\n\r\n* **Should provide reasonable error messages**:\r\nhere's what the error messages look like:\r\n```\r\nerror:\r\n    • Couldn't match expected type ‘((),\r\n                                     (((),\r\n                                       (((), ((), ())),\r\n                                        ((),\r\n                                         (((),\r\n                                           (((),\r\n                                             (a,\r\n                                              ((),\r\n                                               (((), (b, ((), ((), ())))),\r\n                                                ((), (c, ((), (d, ())))))))),\r\n                                            ())),\r\n                                          ())))),\r\n                                      ()))’\r\n                  with actual type ‘(a, b, c, d)’\r\n```\r\nThe error messages aren't actually *that* bad, the types are. I usually copy\r\npaste the type from the error message as the value-level tuple. *Ew.*\r\n\r\n## Map based\r\n\r\n\r\nWe replace all (named) values of type `m a` with a value of type `m (Map String\r\na)`. Then we append the maps together and return it to the user. The user can\r\nthen lookup values using a `String` as the key. Obviously the main drawback\r\nhere is that we lose some safety: the user gets a `Maybe a` from `lookup` even\r\nthough we should be able to assure that a key/value pair is present. Also, I'm\r\nnot sure how to deal with the fact that all values have different types.\r\n\r\nI haven't tried implementing this one, so I won't say too much.\r\n\r\nEasy to implement\r\n\r\n* **Doesn't introduce too much new syntax**: I don't think so, though it's\r\n  unclear what the implementation would look like.\r\n\r\n* **Doesn't require extra type signatures or too much boilerplate to make\r\n  work**: The user needs to pattern match on the `Maybe`.\r\n\r\n* **Easy to reason about - i.e. not too \"magical\"**: The user just needs to\r\n  know how to use `lookup :: Map k v -> k -> Maybe v`\r\n\r\n* **Should provide reasonable error messages**: There shouldn't be any new\r\n  error message\r\n\r\n## Pattern match and NamedFieldPuns\r\n\r\nI used to really like that one, however now I see a few flaws with the design. First,\r\nwe need a constructor to pattern match on. That is you can't just call\r\n\r\n``` haskell\r\n{x, y} <- [jsx|<div>x@{return 2}y@{return 3}</div>\r\n```\r\n\r\ninstead you'd need\r\n\r\n``` haskell\r\nC {x, y} <- [jsx|<div>x@{return 2}y@{return 3}</div>\r\n```\r\n\r\nThere are several ways to fix it. The first one is to let the user provide\r\neither a datatype or even a default value, as you suggest in `Idea 1`. The\r\nsecond one is to let TH generate a datatype and corresponding constructor. How\r\ndo we do that? Does the user pass in a String that will be the Constructor's\r\nname?\r\n\r\n``` haskell\r\nACons {x, y} <- [jsx|(ACons)<div>x@{return 2}y@{return 3}</div>|]\r\n```\r\n\r\nEither way TH is \"happening\" outside the quote and either is allowed to modify\r\nthe environment, like adding new functions into scope, or the user has to\r\nsomehow leave clues to tell the quasiquote what to generate.\r\n\r\n* **Doesn't introduce too much new syntax**: User needs to specify the return\r\n  type and/or type constructor.\r\n\r\n* **Doesn't require extra type signatures or too much boilerplate to make\r\n  work**: There *is* a significant amount of boilerplate.\r\n\r\n* **Easy to reason about - i.e. not too \"magical\"**: That depends on whether or\r\n  not you consider TH magical. It can definitely be opaque.\r\n\r\n* **Should provide reasonable error messages**: I guess it would?\r\n\r\n## Symbol indexed lists\r\n\r\nThe `[jsx|...|]` returns a (heterogeneous) list of all the named values.\r\nHowever the values are tagged with the name they were given, at the type level:\r\n\r\n``` haskell\r\nex1 :: (MonadWidget t m) => m (HList '[ '(Int, \"x\")])\r\nex1 = [jsx|<div>x@{return (1 :: Int)}|]\r\n\r\nex2 :: (MonadWidget t m) => m (HList '[ '(Int, \"x\"), '(String, \"str\")])\r\nex2 = [jsx|<div>x@{return (1 :: Int)}<p>str@{return \"1234\"}</p>|]\r\n```\r\n\r\nUsing type application we can retrieve the values, indexing into the list with `Symbol`s:\r\n\r\n\r\n``` haskell\r\nex3 :: (MonadWidget t m) => m Int\r\nex3 = (unWrap @\"x\" . extract) <$> [jsx|<div>x@{return (1 :: Int)}<p>str@{return \"1234\"}</p>|]\r\n\r\nex4 :: (MonadWidget t m) => m (Dynamic t Bool)\r\nex4 = (unWrap @\"check\" . extract) <$> [jsx|<div>\r\n                                              x@{return anInt}\r\n                                              <p>\r\n                                                check@{_checkbox_value <$> checkbox True def}\r\n                                              </p>\r\n                                            </div>|]\r\n```\r\n\r\nHere's a [proof of\r\nconcept](https://github.com/dackerman/reflex-jsx/compare/master...nmattia:nm/sym-index).\r\n\r\nIt is actually very similar to the tuple idea.\r\n\r\n* **Doesn't introduce too much new syntax**: Need to enable `TypeApplications`\r\n\r\n* **Doesn't require extra type signatures or too much boilerplate to make\r\n  work**: There is some extra type signature, but it is part of the design.\r\n  There might be a way to get rid of the two function calls and have something\r\n  as follows:\r\n``` haskell\r\nex5 = unWrap @\"foo\" <$> [jsx|<div>foo@{...}</div>|]\r\n```\r\n\r\n* **Easy to reason about - i.e. not too \"magical\"**: This once again depends on\r\n  what's considered magical. Once the `[jsx|...|]` block has returned a value\r\n  we do not rely on TH anymore. However `TypeApplications` is a pretty recent\r\n  addition to GHC.\r\n\r\n\r\n* **Should provide reasonable error messages**: The type messages should be\r\n  straightforward, effectively telling you that `\"foo\"` is not contained in\r\n  `[\"bar\", \"baz\"]`.\r\n\r\nTo be honest, I really like the last solution. It is type safe and uses non-TH,\r\nstandard Haskell mechanisms. We could even provide `Proxy` based functions.\r\n"}}
{"actor":{"display_login":"nmattia"}}