2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE NoMonomorphismRestriction #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE RecursiveDo #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE StandaloneDeriving #-}
14{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeOperators #-}
16{-# LANGUAGE UndecidableInstances #-}
17#ifdef USE_REFLEX_OPTIMIZER
18{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
24-- This module contains various functions for working with 'Dynamic' values.
25-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
28 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
41 , attachPromptlyDynWith
42 , attachPromptlyDynWithMaybe
56 , joinDynThroughIntMap
60 , distributeMapOverDynPure
61 , distributeIntMapOverDynPure
62 , distributeDMapOverDynPure
63 , distributeListOverDynPure
68 -- Things that probably aren't very useful:
72 , RebuildSortedHList (..)
76 , distributeFHListOverDynPure
81import Data.Functor.Compose
82import Data.Functor.Misc
86import Control.Monad.Fix
87import Control.Monad.Identity
89import Data.Dependent.Map (DMap)
90import qualified Data.Dependent.Map as DMap
91import Data.Dependent.Sum (DSum (..))
92import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
93import Data.IntMap (IntMap)
94import qualified Data.IntMap as IntMap
97import Data.Monoid ((<>))
99import Data.Type.Equality ((:~:) (..))
103-- | Map a sampling function over a 'Dynamic'.
104mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b)
105mapDynM f d = buildDynamic (f =<< sample (current d)) $ pushAlways f (updated d)
107-- | Flipped version of 'mapDynM'
108forDynM :: forall t m a b. (Reflex t, MonadHold t m) => Dynamic t a -> (forall m'. MonadSample t m' => a -> m' b) -> m (Dynamic t b)
109forDynM d f = mapDynM f d
111-- | Create a new 'Dynamic' that only signals changes if the values actually
113holdUniqDyn :: (Reflex t, MonadHold t m, MonadFix m, Eq a) => Dynamic t a -> m (Dynamic t a)
114holdUniqDyn = holdUniqDynBy (==)
116-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
117-- changes and the given function returns 'False' when given both the old and
119holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
120holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
122-- | @/Dynamic Maybe/@ that can only update from @/Nothing/@ to @/Just/@ or @/Just/@ to @/Just/@ (i.e., cannot revert to @/Nothing/@)
123improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
124improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
126-- | Create a 'Dynamic' that accumulates values from another 'Dynamic'. This
127-- function does not force its input 'Dynamic' until the output 'Dynamic' is
129scanDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> b) -> Dynamic t a -> m (Dynamic t b)
130scanDyn z f = scanDynMaybe z (\a b -> Just $ f a b)
132-- | Like 'scanDyn', but the the accumulator function may decline to update the
133-- result 'Dynamic''s value.
134scanDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> Maybe b) -> Dynamic t a -> m (Dynamic t b)
135scanDynMaybe z f d = do
136 rec d' <- buildDynamic (z <$> sample (current d)) $ flip push (updated d) $ \a -> do
137 b <- sample $ current d'
141-- | Create a 'Dynamic' using the initial value and change it each time the
142-- 'Event' occurs using a folding function on the previous value and the value
144foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
145foldDyn = accumDyn . flip
147-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
148-- can 'sample' existing 'Behaviors' and 'hold' new ones.
149foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
150foldDynM = accumMDyn . flip
152-- | Create a 'Dynamic' using the provided initial value and change it each time
153-- the provided 'Event' occurs, using a function to combine the old value with
154-- the 'Event''s value. If the function returns 'Nothing', the value is not
155-- changed; this is distinct from returning 'Just' the old value, since the
156-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
157-- in the 'Nothing' case.
158foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
159foldDynMaybe = accumMaybeDyn . flip
161-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
162-- can 'sample' existing 'Behaviors' and 'hold' new ones.
163foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
164foldDynMaybeM = accumMaybeMDyn . flip
166-- | Create a new 'Dynamic' that counts the occurrences of the 'Event'.
167count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
168count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
170-- | Create a new 'Dynamic' using the initial value that flips its
171-- value every time the 'Event' occurs.
172toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
173toggle = foldDyn (const not)
175-- | Switches to the new 'Event' whenever it receives one. Only the old event is
176-- considered the moment a new one is switched in; the output event will fire at
177-- that moment if only if the old event does.
179-- Prefer this to 'switchPromptlyDyn' where possible. The lack of doing double
180-- work when the outer and (new) inner fires means this imposes fewer "timing
181-- requirements" and thus is far more easy to use without introducing fresh
182-- failure cases. 'switchDyn' is also more performant.
183switchDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
184switchDyn d = switch (current d)
186-- | Switches to the new 'Event' whenever it receives one. Switching occurs
187-- __before__ the inner 'Event' fires - so if the 'Dynamic' changes and both the
188-- old and new inner Events fire simultaneously, the output will fire with the
189-- value of the __new__ 'Event'.
191-- Prefer 'switchDyn' to this where possible. The timing requirements that
192-- switching before imposes are likely to bring down your app unless you are
193-- very careful. 'switchDyn' is also more performant.
194switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
195switchPromptlyDyn de =
196 let eLag = switch $ current de
197 eCoincidences = coincidence $ updated de
198 in leftmost [eCoincidences, eLag]
200-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
201splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
202splitDynPure d = (fmap fst d, fmap snd d)
204-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
205-- non-'Dynamic' elements.
206distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
207distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
209-- | Convert an 'IntMap' with 'Dynamic' elements into a 'Dynamic' of an 'IntMap' with
210-- non-'Dynamic' elements.
211distributeIntMapOverDynPure :: (Reflex t) => IntMap (Dynamic t v) -> Dynamic t (IntMap v)
212distributeIntMapOverDynPure = fmap dmapToIntMap . distributeDMapOverDynPure . intMapWithFunctorToDMap
214-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
215-- non-'Dynamic' elements, preserving the order of the elements.
216{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
217distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
218distributeListOverDynPure = distributeListOverDyn
220--TODO: Generalize this to functors other than Maps
221-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
222-- with the current values of the 'Dynamic's in a map.
223joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
224joinDynThroughMap = (distributeMapOverDynPure =<<)
226-- | Combine a 'Dynamic' of an 'IntMap' of 'Dynamic's into a 'Dynamic'
227-- with the current values of the 'Dynamic's in a map.
228joinDynThroughIntMap :: forall t a. (Reflex t) => Dynamic t (IntMap (Dynamic t a)) -> Dynamic t (IntMap a)
229joinDynThroughIntMap = (distributeIntMapOverDynPure =<<)
231-- | Print the value of the 'Dynamic' when it is first read and on each
232-- subsequent change that is observed (as 'traceEvent'), prefixed with the
233-- provided string. This should /only/ be used for debugging.
235-- Note: Just like Debug.Trace.trace, the value will only be shown if something
236-- else in the system is depending on it.
237traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
238traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
240-- | Print the result of applying the provided function to the value
241-- of the 'Dynamic' when it is first read and on each subsequent change
242-- that is observed (as 'traceEvent'). This should /only/ be used for
245-- Note: Just like Debug.Trace.trace, the value will only be shown if something
246-- else in the system is depending on it.
247traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
249 let e' = traceEventWith f $ updated d
251 x <- sample $ current d
252 trace (f x) $ return x
253 in unsafeBuildDynamic getV0 e'
255-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
256-- each time the 'Event' occurs.
258-- Note: @/tagPromptlyDyn d e/@ differs from @/tag (current d) e/@ in the case that @/e/@ is firing
259-- at the same time that @/d/@ is changing. With @/tagPromptlyDyn d e/@, the __new__ value of @/d/@
260-- will replace the value of @/e/@, whereas with @/tag (current d) e/@, the __old__ value
261-- will be used, since the 'Behavior' won't be updated until the end of the frame.
262-- Additionally, this means that the output 'Event' may not be used to directly change
263-- the input 'Dynamic', because that would mean its value depends on itself. __When creating__
264-- __cyclic data flows, generally @/tag (current d) e/@ is preferred.__
265tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
266tagPromptlyDyn = attachPromptlyDynWith const
268-- | Attach the current value of the 'Dynamic' to the value of the
269-- 'Event' each time it occurs.
271-- Note: @/attachPromptlyDyn d/@ is not the same as @/attach (current d)/@. See 'tagPromptlyDyn' for details.
272attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
273attachPromptlyDyn = attachPromptlyDynWith (,)
275-- | Combine the current value of the 'Dynamic' with the value of the
276-- 'Event' each time it occurs.
278-- Note: @/attachPromptlyDynWith f d/@ is not the same as @/attachWith f (current d)/@. See 'tagPromptlyDyn' for details.
279attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
280attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
282-- | Create a new 'Event' by combining the value at each occurrence with the
283-- current value of the 'Dynamic' value and possibly filtering if the combining
284-- function returns 'Nothing'.
286-- Note: @/attachPromptlyDynWithMaybe f d/@ is not the same as @/attachWithMaybe f (current d)/@. See 'tagPromptlyDyn' for details.
287attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
288attachPromptlyDynWithMaybe f d e =
289 let e' = attach (current d) e
290 in fforMaybe (align e' $ updated d) $ \case
291 This (a, b) -> f a b -- Only the tagging event is firing, so use that
292 These (_, b) a -> f a b -- Both events are firing, so use the newer value
293 That _ -> Nothing -- The tagging event isn't firing, so don't fire
295-- | Factor a @/Dynamic t (Maybe a)/@ into a @/Dynamic t (Maybe (Dynamic t a))/@,
296-- such that the outer 'Dynamic' is updated only when the "Maybe"'s constructor
297-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
298-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
299-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
300-- existing inner 'Dynamic' will become constant, and will not change when the
301-- outer constructor changes back to 'Nothing'.
302maybeDyn :: forall t a m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe (Dynamic t a)))
303maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
311-- | Turns a 'Dynamic t (Either a b)' into a 'Dynamic t (Either (Dynamic t a) (Dynamic t b))' such that
312-- the outer 'Dynamic' is updated only when the 'Either' constructor changes (e.g., from 'Left' to 'Right').
313eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
314eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
315 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
317 LeftTag :=> Compose a -> Left $ coerceDynamic a
318 RightTag :=> Compose b -> Right $ coerceDynamic b
320-- | Factor a 'Dynamic t DSum' into a 'Dynamic' 'DSum' containing nested 'Dynamic' values.
321-- The outer 'Dynamic' updates only when the key of the 'DSum' changes, while the update of the inner
322-- 'Dynamic' represents updates within the current key.
323factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
324 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
325factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
326 update :: Event t (DSum k (Compose (Dynamic t) v))
327 update = flip push (updated d) $ \(newKey :=> newVal) -> do
328 (oldKey :=> _) <- sample (current d)
329 case newKey `geq` oldKey of
330 Just Refl -> return Nothing
331 Nothing -> Just <$> holdKey (newKey :=> newVal)
333 holdKey (k :=> v) = do
334 inner' <- filterEventKey k (updated d)
335 inner <- holdDyn v inner'
336 return $ k :=> Compose inner
338--------------------------------------------------------------------------------
340--------------------------------------------------------------------------------
342-- | Represents a time changing value together with an 'EventSelector' that can
343-- efficiently detect when the underlying 'Dynamic' has a particular value.
344-- This is useful for representing data like the current selection of a long
349-- > demuxed (demux d) k === fmap (== k) d
351-- However, when 'demuxed' is used multiple times, the complexity is only
352-- /O(log(n))/, rather than /O(n)/ for fmap.
353data Demux t k = Demux { demuxValue :: Behavior t k
354 , demuxSelector :: EventSelector t (Const2 k Bool)
357-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
358-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
359demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
360demux k = Demux (current k)
361 (fan $ attachWith (\k0 k1 -> if k0 == k1
363 else DMap.fromList [Const2 k0 :=> Identity False,
364 Const2 k1 :=> Identity True])
365 (current k) (updated k))
367-- | Select a particular output of the 'Demux'; this is equivalent to (but much
368-- faster than) mapping over the original 'Dynamic' and checking whether it is
369-- equal to the given key.
370demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
372 let e = select (demuxSelector d) (Const2 k)
373 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
375--------------------------------------------------------------------------------
377--------------------------------------------------------------------------------
379--TODO: This whole section is badly in need of cleanup
381-- | A heterogeneous list whose type and length are fixed statically. This is
382-- reproduced from the 'HList' package due to integration issues, and because
383-- very little other functionality from that library is needed.
384data HList (l::[*]) where
386 HCons :: e -> HList l -> HList (e ': l)
390type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
391type instance HRevApp '[] l = l
392type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
394hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
396hRevApp (HCons x l) l' = hRevApp l (HCons x l')
398hReverse :: HList l -> HList (HRevApp l '[])
399hReverse l = hRevApp l HNil
401hBuild :: (HBuild' '[] r) => r
404class HBuild' l r where
405 hBuild' :: HList l -> r
407instance (l' ~ HRevApp l '[])
408 => HBuild' l (HList l') where
411instance HBuild' (a ': l) r
412 => HBuild' l (a->r) where
413 hBuild' l x = hBuild' (HCons x l)
415-- | Like 'HList', but with a functor wrapping each element.
417 FHNil :: FHList f '[]
418 FHCons :: f e -> FHList f l -> FHList f (e ': l)
420instance GEq (HListPtr l) where
421 HHeadPtr `geq` HHeadPtr = Just Refl
422 HHeadPtr `geq` HTailPtr _ = Nothing
423 HTailPtr _ `geq` HHeadPtr = Nothing
424 HTailPtr a `geq` HTailPtr b = a `geq` b
426instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
427 HHeadPtr `gcompare` HHeadPtr = GEQ
428 HHeadPtr `gcompare` HTailPtr _ = GLT
429 HTailPtr _ `gcompare` HHeadPtr = GGT
430 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
432-- | A typed index into a typed heterogeneous list.
433data HListPtr l a where
434 HHeadPtr :: HListPtr (h ': t) h
435 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
437deriving instance Eq (HListPtr l a)
438deriving instance Ord (HListPtr l a)
440fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
441fhlistToDMap = DMap.fromList . go
442 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
445 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
447-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
448-- they must be contiguous and sorted.
449class RebuildSortedHList l where
450 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
451 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
453instance RebuildSortedHList '[] where
454 rebuildSortedFHList l = case l of
456 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
457 rebuildSortedHList l = case l of
459 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
461instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
462 rebuildSortedFHList l = case l of
463 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
464 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
465 rebuildSortedHList l = case l of
466 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
467 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
469dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
470dmapToHList = rebuildSortedHList . DMap.toList
472-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
473-- single 'Dynamic' whose value represents the current values of all of the
475distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
476distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
478-- | Indicates that all elements in a type-level list are applications of the
480class AllAreFunctors (f :: a -> *) (l :: [a]) where
481 type FunctorList f l :: [*]
482 toFHList :: HList (FunctorList f l) -> FHList f l
483 fromFHList :: FHList f l -> HList (FunctorList f l)
485instance AllAreFunctors f '[] where
486 type FunctorList f '[] = '[]
487 toFHList l = case l of
489#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
490 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
492 fromFHList FHNil = HNil
494instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
495 type FunctorList f (h ': t) = f h ': FunctorList f t
496 toFHList l = case l of
497 a `HCons` b -> a `FHCons` toFHList b
498#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
499 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
501 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
503-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
504-- single 'Dynamic' whose value represents all the current values of the input's
505-- constituent 'Dynamic's.
506collectDynPure :: ( RebuildSortedHList (HListElems b)
507 , IsHList a, IsHList b
508 , AllAreFunctors (Dynamic t) (HListElems b)
510 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
511 ) => a -> Dynamic t b
512collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
514-- | Poor man's 'Generic's for product types only.
516 type HListElems a :: [*]
517 toHList :: a -> HList (HListElems a)
518 fromHList :: HList (HListElems a) -> a
520instance IsHList (a, b) where
521 type HListElems (a, b) = [a, b]
522 toHList (a, b) = hBuild a b
523 fromHList l = case l of
524 a `HCons` b `HCons` HNil -> (a, b)
525#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
526 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
529instance IsHList (a, b, c, d) where
530 type HListElems (a, b, c, d) = [a, b, c, d]
531 toHList (a, b, c, d) = hBuild a b c d
532 fromHList l = case l of
533 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
534#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
535 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
538instance IsHList (a, b, c, d, e, f) where
539 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
540 toHList (a, b, c, d, e, f) = hBuild a b c d e f
541 fromHList l = case l of
542 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
543#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
544 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list