0

My intention is to alternate the colour of a div between bright red and dark red as a button gets pressed, starting with dark red.

I have this code:

{-# LANGUAGE
    OverloadedStrings
  #-}

module Main where

import Data.Map (Map)
import Reflex.Dom

main = mainWidget $ do
    x <- greenButton
    y <- toggle False x
    let z = fmap style y
    elDynAttr "div" z blank

style :: Bool -> Map String String
style b | b         = "style" =: "height: 10ex; width: 10ex; background-color: #f00;"
        | otherwise = "style" =: "height: 10ex; width: 10ex; background-color: #900;"

greenButton :: MonadWidget t m => m (Event t ())
greenButton = button "[ ]"  -- Should be green but whatever.

It errors out thus:

• No instance for (Functor (Dynamic Spider))
    arising from a use of ‘fmap’
• In the expression: fmap style y
  In an equation for ‘z’: z = fmap style y
  In the second argument of ‘($)’, namely
    ‘do { x <- greenButton;
          y <- toggle False x;
          let z = fmap style y;
          elDynAttr "div" z blank }’

I certainly see an fmap for Dynamic in the quick reference, though I am not sure the version of the reference and the version of the reflex package I compile against are consistent.

This is the stack.yaml I use for building:

resolver: lts-7.19
compiler: ghcjs-0.2.1.9007019_ghc-8.0.1
compiler-check: match-exact

setup-info:
  ghcjs:
    source:
      ghcjs-0.2.1.9007019_ghc-8.0.1:
           url: http://ghcjs.tolysz.org/ghc-8.0-2017-02-05-lts-7.19-9007019.tar.gz
           sha1: d2cfc25f9cda32a25a87d9af68891b2186ee52f9

extra-deps:
- reflex-dom-0.3
- ghcjs-dom-0.2.4.0
- ref-tf-0.4.0.1
- reflex-0.4.0.1

allow-newer: true

What am I doing wrong? And who the hell is this guy Spider?

sjakobi
  • 3,546
  • 1
  • 25
  • 43
Ignat Insarov
  • 4,660
  • 18
  • 37

1 Answers1

2

The discussion below explains why Functor for Dynamic turned out to be a bad idea.

https://github.com/reflex-frp/reflex/pull/39

The problem with this instance is that it will evaluate f twice whenever the input dynamic changes: once to compute the new Event value, and once to compute the new Behavior value. With mapDyn, there is only a single computation, the result of both is shared. This is also why mapDyn is monadic.


So the code would look like this:

main = mainWidget $ do
    x <- greenButton
    y <- toggle False x
    z <- mapDyn style y      -- monadic mapDyn instead of fmap
    elDynAttr "div" z blank

On the issue of documentation, Quickref.md for version 0.4.0.1 is here and only references mapDyn.

The Quickref.md you linked to is the one for the future reflex-0.5 (the current develop branch), which is reworking Dynamic to have a Functor instance. See their wiki:

Why doesn't Dynamic have Functor/Applicative/Monad instances

Reflex is scheduled to get these instances in version 0.5. As of this writing the implementation is mostly complete and is undergoing final polish and testing before release and is currently available on github in reflex's develop branch.

Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
  • Oh. So why did they not fix the Quickref? It's been 2 years. I guess I should post an issue. – Ignat Insarov Apr 13 '18 at 11:07
  • Probably an oversight. – Li-yao Xia Apr 13 '18 at 11:42
  • It will be easier for me to accept your answer if you could expand it somewhat. One thing that would be helpful to a future reader is an example of working code. Also, it would be great if you refer to the [issue](https://github.com/reflex-frp/reflex/issues/187) that I created, in case there will be answers from the maintainers, so that the answer stays up to date. I could edit the question myself, but it is questionable practice to significantly expand other people's answers, and some of the reviewers may vote against the edit. – Ignat Insarov Apr 13 '18 at 13:17
  • Okay I added a bit more meat. It turns out the doc question is not an oversight, but that the linked `Quickref.md` is for a version of `reflex` under development. – Li-yao Xia Apr 13 '18 at 13:47
  • For the record, I don't mind edits by other people to improve my answers, and otherwise I welcome requests to do so if you don't feel confident about it. – Li-yao Xia Apr 13 '18 at 13:56
  • The problem is that some picky reviewers mind these edits if they are too massive and add substantial content of their own. I will keep your will in mind, but I would rather stick to asking you to expand your posts rather than doing so myself. – Ignat Insarov Apr 13 '18 at 14:05
  • I see. That is fine too. – Li-yao Xia Apr 13 '18 at 14:11