Reflection을 사용하여 Haskell 응용 프로그램을 만듭니다.제2부분
41611 단어 tutorialprogramminghaskellfunctional
안녕하세요!반사 기반 웹 응용 프로그램 개발에 주력하기 위해 일련의 강좌를 계속합시다.
이 부분에서, 우리는 작업 목록에 각종 조작을 수행할 수 있는 능력을 추가할 것이다.
Todo의 작업
작업을 완료됨으로 선택하고 편집하고 삭제할 수 있습니다.
우선, 상태를 추가해서 Todo
유형을 확장합니다.작업이 완료되지 않은 경우 편집할 수 있습니다.
data TodoState
= TodoDone
| TodoActive { stateEdit :: Bool }
deriving (Generic, Eq, Show)
data Todo = Todo
{ todoText :: Text
, todoState :: TodoState }
deriving (Generic, Eq, Show)
newTodo :: Text -> Todo
newTodo todoText = Todo { todoState = TodoActive False, .. }
그리고 시스템에서 발생하는 이벤트를 정의합니다.우리의 상업 프로젝트에서, 우리는 이를 위해 두 가지 방법을 사용했다.첫 번째 방법은 모든 가능한 사건을 하나의 구조 함수로 매거하고 처리 함수를 실현하는 것을 의미하며 이 함수는 발생하는 이벤트에 따라 상태를 업데이트한다.
data TodoEvent
= NewTodo Todo
| ToggleTodo Int
| StartEditTodo Int
| FinishEditTodo (Text,Int)
| DeleteTodo Int
deriving (Generic, Eq, Show)
이런 방법의 장점은 시스템에서 구체적으로 어떤 사건이 발생했는지, 그리고 그것이 가지고 있는 업데이트를 볼 수 있다는 것을 포함한다. (모든 것은 traceEvent
함수로 이루어진 것이다.)그러나 이러한 장점을 이용하는 것은 항상 가능한 것이 아니다. 특히 사건이 최종적으로 분석하기 어려운 대량의 데이터를 포함할 때.값의 변화를 볼 필요가 있다면, 이벤트는 어떤 상황에서도 변할 수 있습니다. Dynamic
함수 traceDyn
를 사용하여 값을 추적할 수 있습니다.
두 번째 방법은 반군 Endo
으로 표시된 업데이트 함수(대략적으로 말하면 매개 변수와 결과 유형이 겹치는 함수의 추상화)를 사용하는 것이다.이런 방법의 본질은 업데이트 이벤트가 가지고 있는 값이 업데이트 논리 자체를 정의하는 함수라는 것이다.이러한 상황에서 우리는 이벤트 값을 표시하는 능력을 잃었다. (사실은 이 능력이 항상 유용하지 않다는 것을 증명한다.) 그러나 뚜렷한 장점은 현재 상태에 접근하거나 모든 가능한 이벤트를 포함하는 유형을 만들거나, 받은 이벤트에 따라 업데이트된 상태를 정의하는 단일 처리 프로그램이 필요하지 않다는 것이다.
이 강좌에서 우리는 두 번째 방법을 사용할 것이다.
루트 위젯의 구조를 변경합니다.
rootWidget :: MonadWidget t m => m ()
rootWidget =
divClass "container" $ do
elClass "h2" "text-center mt-3" $ text "Todos"
newTodoEv <- newTodoForm
rec
todosDyn <- foldDyn appEndo mempty $ leftmost [newTodoEv, todoEv]
delimiter
todoEv <- todoListWidget todosDyn
blank
우리가 먼저 본 것은 RecursiveDo
확장된 사용 (따라서 사용해야 함) 이다.이것은 reflex
응용 프로그램 개발에서 가장 보편적인 실천 중 하나이다. 왜냐하면 페이지 밑에 발생하는 이벤트가 페이지 맨 위 요소에 영향을 주는 경우가 매우 흔하기 때문이다.이 예에서 이벤트todoEv
는 정의todosDyn
에 사용되고 todosDyn
는 이벤트가 나오는 작은 위젯의 매개 변수입니다.
다음에 우리는 todoEv
함수 매개 변수의 업데이트를 볼 것이다.여기에는 새로운 기능foldDyn
이 적용됐다.이벤트 목록을 받아들여 이벤트 목록에서 이벤트가 발생했을 때 발생한 이벤트를 되돌려줍니다.목록에 있는 두 이벤트가 정해진 시간에 발생하면 가장 왼쪽에 있는 이벤트로 돌아갑니다.퀘스트 열은 목록이 아니라 leftmost
에 나타납니다IntMap
.우선, 이렇게 하는 것은 우리가 표지부를 통해 요소에 직접 접근할 수 있도록 하기 위해서이다.type Todos = IntMap Todo
는 목록을 업데이트하는 데 사용됩니다.각 이벤트를 별도의 구조 함수로 정의할 경우 다음과 같이 처리 함수를 정의해야 합니다.
updateTodo :: TodoEvent -> Todos -> Todos
updateTodo ev todos = case ev of
NewTodo todo -> nextKey todos =: todo <> todos
ToggleTodo ix -> update (Just . toggleTodo) ix todos
StartEditTodo ix -> update (Just . startEdit) ix todos
FinishEditTodo (v, ix) -> update (Just . finishEdit v) ix todos
DeleteTodo ix -> delete ix todos
비록 이 함수를 정의할 필요는 없지만, 우리는 여기서 다른 몇 개의 보조 함수를 사용했다. 어쨌든 우리는 더 많은 기능을 필요로 한다.
startEdit :: Todo -> Todo
startEdit todo = todo { todoState = TodoActive True }
finishEdit :: Text -> Todo -> Todo
finishEdit val todo = todo
{ todoState = TodoActive False, todoText = val }
toggleTodo :: Todo -> Todo
toggleTodo Todo{..} = Todo {todoState = toggleState todoState,..}
where
toggleState = \case
TodoDone -> TodoActive False
TodoActive _ -> TodoDone
nextKey :: IntMap Todo -> Int
nextKey = maybe 0 (succ . fst . fst) . maxViewWithKey
새 요소를 추가하는 함수도 변경되었습니다. 현재 되돌아오는 것은 작업 자체가 아니라 이벤트입니다.새 작업이 추가되면 필드 정리도 추가합니다.
newTodoForm :: MonadWidget t m => m (Event t (Endo Todos))
newTodoForm = rowWrapper $ el "form" $ divClass "input-group" $ mdo
iEl <- inputElement $ def
& initialAttributes .~
( "type" =: "text"
<> "class" =: "form-control"
<> "placeholder" =: "Todo" )
& inputElementConfig_setValue .~ ("" <$ btnEv)
let
addNewTodo = \todo -> Endo $ \todos ->
insert (nextKey todos) (newTodo todo) todos
newTodoDyn = addNewTodo <$> value iEl
btnAttr = "class" =: "btn btn-outline-secondary"
<> "type" =: "button"
(btnEl, _) <- divClass "input-group-append" $
elAttr' "button" btnAttr $ text "Add new entry"
let btnEv = domEvent Click btnEl
pure $ tagPromptlyDyn newTodoDyn $ domEvent Click btnEl
함수 appEndo
가 작업 변경 사항을 되돌려줍니다.그것도 경미한 변화가 생겼다.
todoListWidget :: MonadWidget t m => Dynamic t Todos -> m (Event t (Endo Todos))
todoListWidget todosDyn = rowWrapper $ do
evs <- listWithKey
(M.fromAscList . IM.toAscList <$> todosDyn) todoWidget
pure $ switchDyn $ leftmost . M.elems <$> evs
우리가 발견한 첫 번째 일은 todoListWidget
함수가 simpleList
대체되는 것이다.그것들은 첫 번째 매개 변수의 유형에 있어서 서로 다르다. 첫 번째 함수 수용 목록 listWithKey
, 두 번째 함수 수용 목록 []
.목록은 키를 눌러 정렬됩니다.여기서 되돌아오는 값은 매우 중요하다.모든 작업이 이벤트 (삭제, 변경) 를 되돌려줍니다.구체적인 경우Map
함수는 다음과 같습니다.
listWithKey
:: MonadWidget t m
=> Dynamic t (Map Int Todo)
-> (Int -> Dynamic t Todo -> m (Event t TodoEvent))
-> m (Dynamic t (Map Int TodoEvent))
Note: this function is a part of the reflex
package and has a more complex type. Here we show the specialized type.
여기서 우리는 모든 listWithKey
값에 대해 익숙한 leftmost
함수를 사용한다.표현식Map
의 유형은 다음과 같다: leftmost . elems <$> evs
.우리는 함수Dynamic t (Event t TodoEvent)
를 사용하여 switchDyn
에서 검색Event
을 한다.이 함수는 내부 이벤트가 발생했을 때 발생한 이벤트를 되돌려줍니다.Dynamic
와 Dynamic
가 동시에 발생하면 Event
의 이벤트가 업데이트될 때까지 이전 Event
로 돌아갑니다.함수Dynamic
의 조작 방식은 다르다. switchPromtlyDyn
업데이트, Dynamic
업데이트 전에 존재하는 이벤트의 발생과 현재 포함Dynamic
의 이벤트의 트리거가 동시에 발생하면 포함Dynamic
의 새로운 이벤트를 되돌려준다.만약 이런 상황이 불가능하다면 Dynamic
를 사용하는 것이 가장 좋다. switchDyn
함수가 더욱 복잡하기 때문에 추가 작업을 수행할 수 있고 순환을 만들 수 있기 때문이다.
임무가 다른 상태를 얻었는데 이것이 바로 임무를 대표하는 함수도 왜 바뀌었는가이다.
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t (Endo Todos))
todoWidget ix todoDyn = do
todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
TodoDone -> todoDone ix todoText
TodoActive False -> todoActive ix todoText
TodoActive True -> todoEditable ix todoText
switchHold never todoEvEv
여기서 우리가 사용하는 것은 새로운 함수switchPromtlyDyn
다.다음과 같은 유형이 있습니다.
dyn
:: (Adjustable t m, NotReady t m, PostBuild t m)
=> Dynamic t (m a)
-> m (Event t a)
그것은 dyn
에 봉인된 작은 위젯을 입력 매개 변수로 받아들인다.이는 매번 업데이트Dynamic
이후에Dynamic
업데이트가 있다는 것을 의미한다.출력 값은 작은 위젯이 되돌아오는 값을 가진 이벤트입니다.우리의 예에서 전용 유형은 다음과 같다.
dyn
:: MonadWidget t m
=> Dynamic t (m (Event t (Endo Todos)))
-> m (Event t (Event t (Endo Todos)))
여기서 우리는 다른 사건에 끼워 넣은 사건을 만났다.DOM
패키지의 두 함수는 이런 유형에 대해 작업을 수행할 수 있다. reflex
와 coincidence
.첫 번째 함수는 외부와 내부 이벤트가 동시에 발생할 때만 발생하는 이벤트를 되돌려줍니다.이것은 우리의 상황이 아니다.기능switchHold
의 유형은 다음과 같습니다.
switchHold :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t)
이 함수는 외부 이벤트가 발생할 때마다 새 이벤트로 전환됩니다.외부 이벤트가 처음 발생할 때까지 첫 번째 매개 변수로 전달되는 이벤트는 유지됩니다.이것이 바로 우리가 예시에서 이 함수를 사용하는 방식이다.목록을 처음 변경하기 전에 목록에 이벤트가 나타나지 않기 때문에 switchHold
이벤트를 사용합니다.말 그대로 한 번도 없었던 특별한 사건이었다.
함수never
는 서로 다른 상태에 대해 서로 다른 작은 부품을 사용한다.
todoActive :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoActive ix todoText = divClass "d-flex border-bottom" $ do
divClass "p-2 flex-grow-1 my-auto" $
text todoText
divClass "p-2 btn-group" $ do
(doneEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Done"
(editEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Edit"
(delEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Drop"
pure $ Endo <$> leftmost
[ update (Just . toggleTodo) ix <$ domEvent Click doneEl
, update (Just . startEdit) ix <$ domEvent Click editEl
, delete ix <$ domEvent Click delEl
]
todoDone :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoDone ix todoText = divClass "d-flex border-bottom" $ do
divClass "p-2 flex-grow-1 my-auto" $
el "del" $ text todoText
divClass "p-2 btn-group" $ do
(doneEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Undo"
(delEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Drop"
pure $ Endo <$> leftmost
[ update (Just . toggleTodo) ix <$ domEvent Click doneEl
, delete ix <$ domEvent Click delEl
]
todoEditable :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoEditable ix todoText = divClass "d-flex border-bottom" $ do
updTodoDyn <- divClass "p-2 flex-grow-1 my-auto" $
editTodoForm todoText
divClass "p-2 btn-group" $ do
(doneEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Finish edit"
let updTodos = \todo -> Endo $ update (Just . finishEdit todo) ix
pure $
tagPromptlyDyn (updTodos <$> updTodoDyn) (domEvent Click doneEl)
editTodoForm :: MonadWidget t m => Text -> m (Dynamic t Text)
editTodoForm todo = do
editIEl <- inputElement $ def
& initialAttributes .~
( "type" =: "text"
<> "class" =: "form-control"
<> "placeholder" =: "Todo")
& inputElementConfig_initialValue .~ todo
pure $ value editIEl
여기에 사용된 모든 함수는 이전에 설명되어 있기 때문에 우리는 모든 함수를 상세하게 설명하지 않을 것이다.
최적화
다시 todoWidget
함수로 돌아가겠습니다.
listWithKey
:: MonadWidget t m
=> Dynamic t (Map Int Todo)
-> (Int -> Dynamic t Todo -> m (Event t TodoEvent))
-> m (Dynamic t (Map Int TodoEvent))
이 기능은 전송된 데이터listWithKey
에 대한 모든 업데이트가 각 개별 요소에 대한 업데이트를 시작하는 방식으로 작동합니다.개별 요소를 변경해도 값이 변경되지 않음에도 불구하고 업데이트는 각 요소에 전달됩니다.이제 Dynamic
함수로 돌아갑시다.
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t (Endo Todos))
todoWidget ix todoDyn = do
todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
TodoDone -> todoDone ix todoText
TodoActive False -> todoActive ix todoText
TodoActive True -> todoEditable ix todoText
switchHold never todoEvEv
기억하시는 바와 같이 todoWidget
함수는 업데이트할 때마다 dyn
업데이트됩니다.목록의 한 요소의 변경 사항이 각각 요소에 전달되는 것을 감안하면 작업의 전체 DOM
부분이 재구성됩니다. (브라우저의 '개발자' 패널을 사용하여 검사할 수 있습니다.)분명히 이것은 우리가 원하는 것이 아니다.이때todoDyn
기능이 구조 역할을 한다.
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t TodoEvent)
todoWidget ix todoDyn' = do
todoDyn <- holdUniqDyn todoDyn'
todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
TodoDone -> todoDone ix td
TodoActive False -> todoActive ix td
TodoActive True -> todoEditable ix td
switchHold never todoEvEv
우리는 이미 이 줄을 추가했다. DOM
이게 어떻게 된 일입니까?문제는 holdUniqDyn
가 실행 중이지만 포함된 값은 변하지 않는다는 것이다.함수todoDyn <- holdUniqDyn todoDyn'
는 이렇게 작동하기 때문에 전달된Dynamic
이 실행되고 값이 바뀌지 않으면 출력holdUniqDyn
이 실행되지 않기 때문에 우리의 예에서Dynamic
는 불필요하게 재구성되지 않는다.
우리가 얻은 결과는 볼 수 있다in our repository.
다음 부분에서 우리는 사건을 관리하는 또 다른 방법과 GHCJS-DOM 라이브러리의 사용을 고려할 것이다.
Reference
이 문제에 관하여(Reflection을 사용하여 Haskell 응용 프로그램을 만듭니다.제2부분), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/typeable/creating-a-haskell-application-using-reflex-part-2-4jaa
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
data TodoState
= TodoDone
| TodoActive { stateEdit :: Bool }
deriving (Generic, Eq, Show)
data Todo = Todo
{ todoText :: Text
, todoState :: TodoState }
deriving (Generic, Eq, Show)
newTodo :: Text -> Todo
newTodo todoText = Todo { todoState = TodoActive False, .. }
data TodoEvent
= NewTodo Todo
| ToggleTodo Int
| StartEditTodo Int
| FinishEditTodo (Text,Int)
| DeleteTodo Int
deriving (Generic, Eq, Show)
rootWidget :: MonadWidget t m => m ()
rootWidget =
divClass "container" $ do
elClass "h2" "text-center mt-3" $ text "Todos"
newTodoEv <- newTodoForm
rec
todosDyn <- foldDyn appEndo mempty $ leftmost [newTodoEv, todoEv]
delimiter
todoEv <- todoListWidget todosDyn
blank
updateTodo :: TodoEvent -> Todos -> Todos
updateTodo ev todos = case ev of
NewTodo todo -> nextKey todos =: todo <> todos
ToggleTodo ix -> update (Just . toggleTodo) ix todos
StartEditTodo ix -> update (Just . startEdit) ix todos
FinishEditTodo (v, ix) -> update (Just . finishEdit v) ix todos
DeleteTodo ix -> delete ix todos
startEdit :: Todo -> Todo
startEdit todo = todo { todoState = TodoActive True }
finishEdit :: Text -> Todo -> Todo
finishEdit val todo = todo
{ todoState = TodoActive False, todoText = val }
toggleTodo :: Todo -> Todo
toggleTodo Todo{..} = Todo {todoState = toggleState todoState,..}
where
toggleState = \case
TodoDone -> TodoActive False
TodoActive _ -> TodoDone
nextKey :: IntMap Todo -> Int
nextKey = maybe 0 (succ . fst . fst) . maxViewWithKey
newTodoForm :: MonadWidget t m => m (Event t (Endo Todos))
newTodoForm = rowWrapper $ el "form" $ divClass "input-group" $ mdo
iEl <- inputElement $ def
& initialAttributes .~
( "type" =: "text"
<> "class" =: "form-control"
<> "placeholder" =: "Todo" )
& inputElementConfig_setValue .~ ("" <$ btnEv)
let
addNewTodo = \todo -> Endo $ \todos ->
insert (nextKey todos) (newTodo todo) todos
newTodoDyn = addNewTodo <$> value iEl
btnAttr = "class" =: "btn btn-outline-secondary"
<> "type" =: "button"
(btnEl, _) <- divClass "input-group-append" $
elAttr' "button" btnAttr $ text "Add new entry"
let btnEv = domEvent Click btnEl
pure $ tagPromptlyDyn newTodoDyn $ domEvent Click btnEl
todoListWidget :: MonadWidget t m => Dynamic t Todos -> m (Event t (Endo Todos))
todoListWidget todosDyn = rowWrapper $ do
evs <- listWithKey
(M.fromAscList . IM.toAscList <$> todosDyn) todoWidget
pure $ switchDyn $ leftmost . M.elems <$> evs
listWithKey
:: MonadWidget t m
=> Dynamic t (Map Int Todo)
-> (Int -> Dynamic t Todo -> m (Event t TodoEvent))
-> m (Dynamic t (Map Int TodoEvent))
Note: this function is a part of the reflex
package and has a more complex type. Here we show the specialized type.
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t (Endo Todos))
todoWidget ix todoDyn = do
todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
TodoDone -> todoDone ix todoText
TodoActive False -> todoActive ix todoText
TodoActive True -> todoEditable ix todoText
switchHold never todoEvEv
dyn
:: (Adjustable t m, NotReady t m, PostBuild t m)
=> Dynamic t (m a)
-> m (Event t a)
dyn
:: MonadWidget t m
=> Dynamic t (m (Event t (Endo Todos)))
-> m (Event t (Event t (Endo Todos)))
switchHold :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t)
todoActive :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoActive ix todoText = divClass "d-flex border-bottom" $ do
divClass "p-2 flex-grow-1 my-auto" $
text todoText
divClass "p-2 btn-group" $ do
(doneEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Done"
(editEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Edit"
(delEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Drop"
pure $ Endo <$> leftmost
[ update (Just . toggleTodo) ix <$ domEvent Click doneEl
, update (Just . startEdit) ix <$ domEvent Click editEl
, delete ix <$ domEvent Click delEl
]
todoDone :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoDone ix todoText = divClass "d-flex border-bottom" $ do
divClass "p-2 flex-grow-1 my-auto" $
el "del" $ text todoText
divClass "p-2 btn-group" $ do
(doneEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Undo"
(delEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Drop"
pure $ Endo <$> leftmost
[ update (Just . toggleTodo) ix <$ domEvent Click doneEl
, delete ix <$ domEvent Click delEl
]
todoEditable :: MonadWidget t m => Int -> Text -> m (Event t (Endo Todos))
todoEditable ix todoText = divClass "d-flex border-bottom" $ do
updTodoDyn <- divClass "p-2 flex-grow-1 my-auto" $
editTodoForm todoText
divClass "p-2 btn-group" $ do
(doneEl, _) <- elAttr' "button"
( "class" =: "btn btn-outline-secondary"
<> "type" =: "button" ) $ text "Finish edit"
let updTodos = \todo -> Endo $ update (Just . finishEdit todo) ix
pure $
tagPromptlyDyn (updTodos <$> updTodoDyn) (domEvent Click doneEl)
editTodoForm :: MonadWidget t m => Text -> m (Dynamic t Text)
editTodoForm todo = do
editIEl <- inputElement $ def
& initialAttributes .~
( "type" =: "text"
<> "class" =: "form-control"
<> "placeholder" =: "Todo")
& inputElementConfig_initialValue .~ todo
pure $ value editIEl
listWithKey
:: MonadWidget t m
=> Dynamic t (Map Int Todo)
-> (Int -> Dynamic t Todo -> m (Event t TodoEvent))
-> m (Dynamic t (Map Int TodoEvent))
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t (Endo Todos))
todoWidget ix todoDyn = do
todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
TodoDone -> todoDone ix todoText
TodoActive False -> todoActive ix todoText
TodoActive True -> todoEditable ix todoText
switchHold never todoEvEv
todoWidget :: MonadWidget t m => Int -> Dynamic t Todo -> m (Event t TodoEvent)
todoWidget ix todoDyn' = do
todoDyn <- holdUniqDyn todoDyn'
todoEvEv <- dyn $ ffor todoDyn $ \td@Todo{..} -> case todoState of
TodoDone -> todoDone ix td
TodoActive False -> todoActive ix td
TodoActive True -> todoEditable ix td
switchHold never todoEvEv
Reference
이 문제에 관하여(Reflection을 사용하여 Haskell 응용 프로그램을 만듭니다.제2부분), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/typeable/creating-a-haskell-application-using-reflex-part-2-4jaa텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)