在reflex-dom中,如何将值从动态传递到外部javascript函数(FFI)

时间:2017-11-25 23:40:54

标签: haskell ffi reflex

我是Haskell和反射的新手,但非常喜欢这种语言。我一直在使用https://github.com/hansroland/reflex-dom-inbits/blob/master/tutorial.md来学习,这非常有帮助。

我目前正在尝试创建一个函数,该函数接受动态并创建一个元素,并在每次动态值发生变化时调用FFI函数。这是我正在尝试做的简化版本。

{-# LANGUAGE OverloadedStrings #-}
import Data.Text as T
import qualified GHCJS.DOM.Types as GDT
import GHCJS.Types
import Reflex.Dom

foreign import javascript safe
  "$1.value = $2"
  testSet :: JSVal -> JSVal -> IO()

testTB :: DomBuilder t m => Dynamic t T.Text -> m ()
testTB dt = do
  (e, _) <- elAttr' "input" ("type" =: "text") blank
  bob <- (testSet (GDT.pToJSVal e) . GDT.pToJSVal) <$> dt
  return ()

main = mainWidget $ testTB $ constDyn "Hello World!"

这会导致编译时错误:

reflex-canvas.hs:14:10: error:
    • Couldn't match type ‘m’ with ‘Dynamic t’
      ‘m’ is a rigid type variable bound by
        the type signature for:
          testTB :: forall t (m :: * -> *).
                    DomBuilder t m =>
                    Dynamic t Text -> m ()
        at reflex-canvas.hs:11:11
      Expected type: m (IO ())
        Actual type: Dynamic t (IO ())
    • In a stmt of a 'do' block:
        bob <- (testSet (GDT.pToJSVal e) . GDT.pToJSVal) <$> dt
      In the expression:
        do { (e, _) <- elAttr' "input" ("type" =: "text") blank;
             bob <- (testSet (GDT.pToJSVal e) . GDT.pToJSVal) <$> dt;
             return () }
      In an equation for ‘testTB’:
          testTB dt
            = do { (e, _) <- elAttr' "input" ("type" =: "text") blank;
                   bob <- (testSet (GDT.pToJSVal e) . GDT.pToJSVal) <$> dt;
                   return () }
    • Relevant bindings include
        e :: Element EventResult (DomBuilderSpace m) t
          (bound at reflex-canvas.hs:13:4)
        dt :: Dynamic t Text (bound at reflex-canvas.hs:12:8)
        testTB :: Dynamic t Text -> m () (bound at reflex-canvas.hs:12:1)

我已尝试过各种方法将Dynamic转换为m()但无法弄明白。这样做的最佳做法是什么?

1 个答案:

答案 0 :(得分:2)

函数performEvent_将强制执行javascript函数,但performEvent_需要Event t (WidgetHost m ()),并且正如错误消息所指出的那样,您已获得Dynamic t (IO ()) }。

您可以使用updatedDynamic t (IO ())转换为Event t (IO ()),然后使用fmap liftIO更改IO ()中的EventWidgetHost m ()离开Event t (WidgetHost m ()),您可以将其传递给performEvent_

这是您修改后的代码。我删除了testSet的第一个参数和testTB中的元素创建,因为它们与问题/解决方案无关。我还添加了一些额外的类型声明。这些不是必需的,但可以使事情更清楚。

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Text as T (Text)
import qualified GHCJS.DOM.Types as GDT (pToJSVal)
import GHCJS.Types (JSVal)
import Reflex.Dom
import Control.Monad.Trans (liftIO)

foreign import javascript safe
  "console.log $1"
  testSet :: JSVal -> IO()

testTB :: forall t m.  MonadWidget t m => Dynamic t T.Text -> m ()
testTB dt = do 
    let bob :: Dynamic t (IO ())
        bob = (testSet.(GDT.pToJSVal)) <$> dt  

        bobIOEvent :: Event t (IO ())
        bobIOEvent = updated bob

        bobWidgetHostEvent :: Event t (WidgetHost m ())
        bobWidgetHostEvent = fmap liftIO bobIOEvent

    performEvent_ bobWidgetHostEvent

main = mainWidget $ do
    ti <- textInput def 
    let dt = value ti
    testTB dt