[英]How do you use the Bounded typeclass in Haskell to define a type with a floating point range?
[英]How do you define a Haskell typeclass with a type that cannot be deduced?
我正在使用Reflex.Dom库 ,该库定义了一组用于创建HTML DOM元素的函数
el
创建一个元素 el'
创建并返回一个元素 elAttr
创建具有给定属性的元素 elAttr'
创建并返回具有给定属性的元素 我正在创建自己的窗口小部件库,但不想为每个窗口小部件定义所有这些变体。 因此,我编写了一个使用相同名称的类型类,但彼此定义了所有函数,而在每个实例中仅定义了其中一个:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
module ElMaker where
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Reflex.Dom as D
-- el: type of element to create
-- input: input parameter
-- output: return value
class (D.MonadWidget t m) => ElMaker t m el input output where
el :: el -> input -> m output
el e = elAttr e Map.empty
elAttr :: el -> Map Text Text -> input -> m output
elAttr e attrs input = snd <$> elAttr' e attrs input
el' :: el -> input -> m (D.El t, output)
el' e = elAttr' e Map.empty
-- This is the only one to implement, yay!
elAttr' :: el -> Map Text Text -> input -> m (D.El t, output)
我创建了一个使用原始elAttr'
进行测试的实例。 有效:
import Data.Text (Text)
import qualified Reflex.Dom as D
instance (D.MonadWidget t m) => ElMaker t m Text (m output) output where
elAttr' = D.elAttr'
然后,我创建了一个Button
小部件实例,该实例返回单击按钮时的事件。 有效:
data Button = Button
instance (MonadWidget t m) => ElMaker t m Button (m input) (Event t ()) where
elAttr' _ attrs contents = do
(e, _) <- D.el' "button" contents
return $ (e, D.domEvent D.Click e)
我希望能够编写小部件,因此我尝试重写Button
实例以使用ElMaker
的Text
实例创建元素。 但是它无法编译:
data Button = Button
instance (MonadWidget t m) => ElMaker t m Button (m input) (Event t ()) where
elAttr' _ attrs contents = do
(e, _) <- el' ("button" :: Text) contents
return $ (e, D.domEvent D.Click e)
编译器输出:
MDL.hs:119:15: error:
• Could not deduce (ElMaker t m Text (m input) output0)
arising from a use of ‘el'’
from the context: MonadWidget t m
bound by the instance declaration at MDL.hs:116:10-71
The type variable ‘output0’ is ambiguous
Relevant bindings include
contents :: m input (bound at MDL.hs:117:19)
elAttr' :: Button
-> Map.Map Text Text -> m input -> m (D.El t, Event t ())
(bound at MDL.hs:117:3)
These potential instance exist:
instance MonadWidget t m => ElMaker t m Text (m output) output
-- Defined in ‘ElMaker’
• In a stmt of a 'do' block:
(e, _) <- el' ("button" :: Text) contents
In the expression:
do { (e, _) <- el' ("button" :: Text) contents;
return $ (e, D.domEvent D.Click e) }
In an equation for ‘elAttr'’:
elAttr' _ attrs contents
= do { (e, _) <- el' ("button" :: Text) contents;
return $ (e, D.domEvent D.Click e) }
我认为这是因为函数对约束其类型的值不执行任何操作,并且编译器确实希望它具有具体的类型。 但是,此类型类并不关心该类型参数的值是什么。 反正有什么办法编译吗?
您可能想做的事情(这是您经常要做的事情;它正在成为FAQ),是用左侧的等式约束替换=>
右侧的构造函数。
{-# LANGUAGE GADTs #-}
instance (D.MonadWidget t m, input ~ m output)
=> ElMaker t m Text input output where ...
instance (D.MonadWidget t m, input' ~ m input, output ~ Event t ())
=> ElMaker t m Button input' output where ...
一旦知道要构建Text
或Button
,便要提交给特定实例,然后提交给具有特定形状的某些类参数。 将这些放置在实例约束中就可以做到这一点。
对于这里的特殊情况,一旦知道要处理Text
,就知道要使用哪个实例,并且可以通过匹配input
来计算output
。 您希望GHC知道这一点,而不是想知道其他一些Text
实例是否具有不同的输入/输出关系。
注:它通常是最好的决定别人去最后关键的类参数。 因此,我将el
作为ElMaker
的最后一个参数。 这对于新类型派生是有益的,并且也是常规的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.