{"action":"created","issue":{"url":"https://api.github.com/repos/japgolly/scalajs-react/issues/256","repository_url":"https://api.github.com/repos/japgolly/scalajs-react","labels_url":"https://api.github.com/repos/japgolly/scalajs-react/issues/256/labels{/name}","comments_url":"https://api.github.com/repos/japgolly/scalajs-react/issues/256/comments","events_url":"https://api.github.com/repos/japgolly/scalajs-react/issues/256/events","html_url":"https://github.com/japgolly/scalajs-react/issues/256","id":133376259,"number":256,"title":"Produce a warning when map is used instead of flatMap with Callback","user":{"login":"ramnivas","id":137002,"avatar_url":"https://avatars.githubusercontent.com/u/137002?v=3","gravatar_id":"","url":"https://api.github.com/users/ramnivas","html_url":"https://github.com/ramnivas","followers_url":"https://api.github.com/users/ramnivas/followers","following_url":"https://api.github.com/users/ramnivas/following{/other_user}","gists_url":"https://api.github.com/users/ramnivas/gists{/gist_id}","starred_url":"https://api.github.com/users/ramnivas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ramnivas/subscriptions","organizations_url":"https://api.github.com/users/ramnivas/orgs","repos_url":"https://api.github.com/users/ramnivas/repos","events_url":"https://api.github.com/users/ramnivas/events{/privacy}","received_events_url":"https://api.github.com/users/ramnivas/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":7,"created_at":"2016-02-13T00:19:59Z","updated_at":"2016-02-21T00:03:24Z","closed_at":null,"body":"This is a follow-up from gitter discussion.\r\n\r\nHaving spent an hour chasing why things didn't work after migration to 0.10.x, I am worried that this class of errors will be a far too easy to make. Consider the following code in a backend class (modified from the actual code to avoid distracting with domain-specific concepts). Here the intention is to update a word list given a new word and its position.\r\n\r\n```scala\r\ndef updateWord(position: Int, word: String): Callback = \r\n  scope.props.map { props =>\r\n    val newList = props.wordList.updated(position, word)\r\n    props.onUpdateWordList(newList)\r\n  }\r\n```\r\n`props.onUpdateWordList` is a function defined in its parent component that returns a `Callback`.\r\nI chased why `onUpdateWordList` isn't getting called (after suspecting a lot of other things, first :worried:). Ah, of course, I am using `map`, when I should have used `flatMap`. Far too easy to make such mistakes and no help from scalac.\r\n\r\nThe core issues is `scalac` converts anything to `Unit`, so what could have been a `Callback[Callback]` was silently treated as `Callback[Unit]` as can be seen from the following reduced example (which compiles fine despite the intention).\r\n\r\n```scala\r\ndef cb1(): Callback = Callback {}\r\ndef cb2(): Callback = Callback {}\r\ndef cb1_map_cb2(): Callback = cb1().map{ _ => cb2()}\r\ndef cb1_fmap_cb2(): Callback = cb1().flatMap{ _ => cb2()}\r\n```\r\n\r\nIt will be nice if there can be a warning for `map` in the above example (similar to what is being issued when someone tries to create a `Callback { Callback { } }`."},"comment":{"url":"https://api.github.com/repos/japgolly/scalajs-react/issues/comments/186705719","html_url":"https://github.com/japgolly/scalajs-react/issues/256#issuecomment-186705719","issue_url":"https://api.github.com/repos/japgolly/scalajs-react/issues/256","id":186705719,"user":{"login":"japgolly","id":202935,"avatar_url":"https://avatars.githubusercontent.com/u/202935?v=3","gravatar_id":"","url":"https://api.github.com/users/japgolly","html_url":"https://github.com/japgolly","followers_url":"https://api.github.com/users/japgolly/followers","following_url":"https://api.github.com/users/japgolly/following{/other_user}","gists_url":"https://api.github.com/users/japgolly/gists{/gist_id}","starred_url":"https://api.github.com/users/japgolly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/japgolly/subscriptions","organizations_url":"https://api.github.com/users/japgolly/orgs","repos_url":"https://api.github.com/users/japgolly/repos","events_url":"https://api.github.com/users/japgolly/events{/privacy}","received_events_url":"https://api.github.com/users/japgolly/received_events","type":"User","site_admin":false},"created_at":"2016-02-21T00:03:24Z","updated_at":"2016-02-21T00:03:24Z","body":"I just had a play with this and this is solvable via a very simple bit of implicit machinery.\r\n\r\n```scala\r\n  final class MapGuard[A] { type Out = A }\r\n\r\n  object MapGuard {\r\n    private val instance = new MapGuard[Any]\r\n    implicit def id[A]: MapGuard[A] =\r\n      instance.asInstanceOf[MapGuard[A]]\r\n  }\r\n\r\n// Then change .map to\r\n  def map[B](g: A => B)(implicit ev: MapGuard[B]): CallbackTo[ev.Out] =\r\n    new CallbackTo(() => g(f()))\r\n```\r\n\r\nThen it presents a new question: should we allow any types to be converted to `Callback` automatically? If not, that will force users to call `.void` manually (or add `()` to their callback bodies). I'm thinking maybe it would be ok to allow `CallbackTo[_ <: AnyVal]` to be converted to `Callback`. Thoughts?"}}