由总和类型值填充的反射下拉菜单

Reflex dropdown menu populated by sum type values

我正在学习本教程,以下示例来自该教程:https://github.com/hansroland/reflex-dom-inbits/blob/master/tutorial.md

bodyElement :: MonadWidget t m => m ()
bodyElement = el "div" $ do
  el "h2" $ text "Dropdown"
  text "Select country "
  dd <- dropdown 2 (constDyn countries) def
  el "p" $ return ()
  let selItem = result <$> value dd
  dynText selItem

countries :: Map.Map Int T.Text
countries = Map.fromList [(1, "France"), (2, "Switzerland"), (3, "Germany"), (4, "Italy"), (5, "USA")]

result :: Int -> T.Text
result key = "You selected: " <> fromJust (Map.lookup key countries)

我想用一个函数替换上面的 constDyn countries,该函数采用求和类型的类型 (?) 构造函数并将它们用作下拉列表的元素。

例如,如果我有以下总和类型,我希望下拉列表显示 "Workout" 和 "Run"。如果我稍后添加 Solo_WatchPracticeTape,下拉列表将自动添加 "Watch Practice Tape".

data SoloPersonPracticeType =
        Solo_Workout 
    |   Solo_Run

我推断我需要创建一个函数将每个总和类型与一个字符串相关联,然后再创建一个函数将所有这些东西变成可以被 constDyn 接受的可遍历的东西。但是我不知道如何实现它。

编辑:

我正在尝试这个,但还没有完成:

displaySoloPersonPracticeType :: SoloPersonPracticeType -> [Char]
displaySoloPersonPracticeType Solo_Workout = "Workout"
displaySoloPersonPracticeType Solo_Run = "Run"

deriving instance Enum SoloPersonPracticeType 
deriving instance Bounded SoloPersonPracticeType 


instance Universe SoloPersonPracticeType where 
    universe = [minBound..]

但我仍然不明白如何将其输入 bodyElement 函数中的 constDyn

类 就像 UniverseEnum 通常是您想要的工具。您实际上想要一种从这些实例生成列表的方法,这非常简单:

[(e, show e) | e <- [minBound..]]

这里 show 假定您的类型有一个 Show 实例。您可以轻松地将其替换为您自己的自定义 "showy" 东西:

showMyType :: MyType -> Text
showMyType = \case
  MyType_A -> "A"
  MyType_B -> "B"
  ...

最后,您只需将整个列表作为 Dynamic t (Map MyType Text) 传递给 dropdown,如下所示:constDyn (Map.fromList [(e, showMyType e) | e <- [minBound..]])