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
59 , distributeMapOverDynPure
60 , distributeDMapOverDynPure
61 , distributeListOverDynPure
66 -- Things that probably aren't very useful:
70 , RebuildSortedHList (..)
74 , distributeFHListOverDynPure
79import Data.Functor.Compose
80import Data.Functor.Misc
84import Control.Monad.Fix
85import Control.Monad.Identity
87import Data.Dependent.Map (DMap)
88import qualified Data.Dependent.Map as DMap
89import Data.Dependent.Sum (DSum (..))
90import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
93import Data.Monoid ((<>))
95import Data.Type.Equality ((:~:) (..))
99-- | Map a sampling function over a 'Dynamic'.
100mapDynM :: 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)
101mapDynM f d = buildDynamic (f =<< sample (current d)) $ pushAlways f (updated d)
103-- | Flipped version of 'mapDynM'
104forDynM :: 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)
105forDynM d f = mapDynM f d
107-- | Create a new 'Dynamic' that only signals changes if the values actually
109holdUniqDyn :: (Reflex t, MonadHold t m, MonadFix m, Eq a) => Dynamic t a -> m (Dynamic t a)
110holdUniqDyn = holdUniqDynBy (==)
112-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
113-- changes and the given function returns 'False' when given both the old and
115holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
116holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
118-- | @/Dynamic Maybe/@ that can only update from @/Nothing/@ to @/Just/@ or @/Just/@ to @/Just/@ (i.e., cannot revert to @/Nothing/@)
119improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
120improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
122-- | Create a 'Dynamic' that accumulates values from another 'Dynamic'. This
123-- function does not force its input 'Dynamic' until the output 'Dynamic' is
125scanDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> b) -> Dynamic t a -> m (Dynamic t b)
126scanDyn z f = scanDynMaybe z (\a b -> Just $ f a b)
128-- | Like 'scanDyn', but the the accumulator function may decline to update the
129-- result 'Dynamic''s value.
130scanDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> Maybe b) -> Dynamic t a -> m (Dynamic t b)
131scanDynMaybe z f d = do
132 rec d' <- buildDynamic (z <$> sample (current d)) $ flip push (updated d) $ \a -> do
133 b <- sample $ current d'
137-- | Create a 'Dynamic' using the initial value and change it each time the
138-- 'Event' occurs using a folding function on the previous value and the value
140foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
141foldDyn = accumDyn . flip
143-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
144-- can 'sample' existing 'Behaviors' and 'hold' new ones.
145foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
146foldDynM = accumMDyn . flip
148-- | Create a 'Dynamic' using the provided initial value and change it each time
149-- the provided 'Event' occurs, using a function to combine the old value with
150-- the 'Event''s value. If the function returns 'Nothing', the value is not
151-- changed; this is distinct from returning 'Just' the old value, since the
152-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
153-- in the 'Nothing' case.
154foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
155foldDynMaybe = accumMaybeDyn . flip
157-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
158-- can 'sample' existing 'Behaviors' and 'hold' new ones.
159foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
160foldDynMaybeM = accumMaybeMDyn . flip
162-- | Create a new 'Dynamic' that counts the occurrences of the 'Event'.
163count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
164count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
166-- | Create a new 'Dynamic' using the initial value that flips its
167-- value every time the 'Event' occurs.
168toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
169toggle = foldDyn (const not)
171-- | Switches to the new 'Event' whenever it receives one. Only the old event is
172-- considered the moment a new one is switched in; the output event will fire at
173-- that moment if only if the old event does.
175-- Prefer this to 'switchPromptlyDyn' where possible. The lack of doing double
176-- work when the outer and (new) inner fires means this imposes fewer "timing
177-- requirements" and thus is far more easy to use without introducing fresh
178-- failure cases. 'switchDyn' is also more performant.
179switchDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
180switchDyn d = switch (current d)
182-- | Switches to the new 'Event' whenever it receives one. Switching occurs
183-- __before__ the inner 'Event' fires - so if the 'Dynamic' changes and both the
184-- old and new inner Events fire simultaneously, the output will fire with the
185-- value of the __new__ 'Event'.
187-- Prefer 'switchDyn' to this where possible. The timing requirements that
188-- switching before imposes are likely to bring down your app unless you are
189-- very careful. 'switchDyn' is also more performant.
190switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
191switchPromptlyDyn de =
192 let eLag = switch $ current de
193 eCoincidences = coincidence $ updated de
194 in leftmost [eCoincidences, eLag]
196-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
197splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
198splitDynPure d = (fmap fst d, fmap snd d)
200-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
201-- non-'Dynamic' elements.
202distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
203distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
205-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
206-- non-'Dynamic' elements, preserving the order of the elements.
207{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
208distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
209distributeListOverDynPure = distributeListOverDyn
211--TODO: Generalize this to functors other than Maps
212-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
213-- with the current values of the 'Dynamic's in a map.
214joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
215joinDynThroughMap = (distributeMapOverDynPure =<<)
217-- | Print the value of the 'Dynamic' when it is first read and on each
218-- subsequent change that is observed (as 'traceEvent'), prefixed with the
219-- provided string. This should /only/ be used for debugging.
221-- Note: Just like Debug.Trace.trace, the value will only be shown if something
222-- else in the system is depending on it.
223traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
224traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
226-- | Print the result of applying the provided function to the value
227-- of the 'Dynamic' when it is first read and on each subsequent change
228-- that is observed (as 'traceEvent'). This should /only/ be used for
231-- Note: Just like Debug.Trace.trace, the value will only be shown if something
232-- else in the system is depending on it.
233traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
235 let e' = traceEventWith f $ updated d
237 x <- sample $ current d
238 trace (f x) $ return x
239 in unsafeBuildDynamic getV0 e'
241-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
242-- each time the 'Event' occurs.
244-- Note: @/tagPromptlyDyn d e/@ differs from @/tag (current d) e/@ in the case that @/e/@ is firing
245-- at the same time that @/d/@ is changing. With @/tagPromptlyDyn d e/@, the __new__ value of @/d/@
246-- will replace the value of @/e/@, whereas with @/tag (current d) e/@, the __old__ value
247-- will be used, since the 'Behavior' won't be updated until the end of the frame.
248-- Additionally, this means that the output 'Event' may not be used to directly change
249-- the input 'Dynamic', because that would mean its value depends on itself. __When creating__
250-- __cyclic data flows, generally @/tag (current d) e/@ is preferred.__
251tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
252tagPromptlyDyn = attachPromptlyDynWith const
254-- | Attach the current value of the 'Dynamic' to the value of the
255-- 'Event' each time it occurs.
257-- Note: @/attachPromptlyDyn d/@ is not the same as @/attach (current d)/@. See 'tagPromptlyDyn' for details.
258attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
259attachPromptlyDyn = attachPromptlyDynWith (,)
261-- | Combine the current value of the 'Dynamic' with the value of the
262-- 'Event' each time it occurs.
264-- Note: @/attachPromptlyDynWith f d/@ is not the same as @/attachWith f (current d)/@. See 'tagPromptlyDyn' for details.
265attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
266attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
268-- | Create a new 'Event' by combining the value at each occurrence with the
269-- current value of the 'Dynamic' value and possibly filtering if the combining
270-- function returns 'Nothing'.
272-- Note: @/attachPromptlyDynWithMaybe f d/@ is not the same as @/attachWithMaybe f (current d)/@. See 'tagPromptlyDyn' for details.
273attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
274attachPromptlyDynWithMaybe f d e =
275 let e' = attach (current d) e
276 in fforMaybe (align e' $ updated d) $ \case
277 This (a, b) -> f a b -- Only the tagging event is firing, so use that
278 These (_, b) a -> f a b -- Both events are firing, so use the newer value
279 That _ -> Nothing -- The tagging event isn't firing, so don't fire
281-- | Factor a @/Dynamic t (Maybe a)/@ into a @/Dynamic t (Maybe (Dynamic t a))/@,
282-- such that the outer 'Dynamic' is updated only when the "Maybe"'s constructor
283-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
284-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
285-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
286-- existing inner 'Dynamic' will become constant, and will not change when the
287-- outer constructor changes back to 'Nothing'.
288maybeDyn :: forall t a m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe (Dynamic t a)))
289maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
297-- | Turns a 'Dynamic t (Either a b)' into a 'Dynamic t (Either (Dynamic t a) (Dynamic t b))' such that
298-- the outer 'Dynamic' is updated only when the 'Either' constructor changes (e.g., from 'Left' to 'Right').
299eitherDyn :: 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)))
300eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
301 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
303 LeftTag :=> Compose a -> Left $ coerceDynamic a
304 RightTag :=> Compose b -> Right $ coerceDynamic b
306-- | Factor a 'Dynamic t DSum' into a 'Dynamic' 'DSum' containing nested 'Dynamic' values.
307-- The outer 'Dynamic' updates only when the key of the 'DSum' changes, while the update of the inner
308-- 'Dynamic' represents updates within the current key.
309factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
310 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
311factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
312 update :: Event t (DSum k (Compose (Dynamic t) v))
313 update = flip push (updated d) $ \(newKey :=> newVal) -> do
314 (oldKey :=> _) <- sample (current d)
315 case newKey `geq` oldKey of
316 Just Refl -> return Nothing
317 Nothing -> Just <$> holdKey (newKey :=> newVal)
319 holdKey (k :=> v) = do
320 inner' <- filterEventKey k (updated d)
321 inner <- holdDyn v inner'
322 return $ k :=> Compose inner
324--------------------------------------------------------------------------------
326--------------------------------------------------------------------------------
328-- | Represents a time changing value together with an 'EventSelector' that can
329-- efficiently detect when the underlying 'Dynamic' has a particular value.
330-- This is useful for representing data like the current selection of a long
335-- > demuxed (demux d) k === fmap (== k) d
337-- However, when getDemuxed is used multiple times, the complexity is only
338-- /O(log(n))/, rather than /O(n)/ for fmap.
339data Demux t k = Demux { demuxValue :: Behavior t k
340 , demuxSelector :: EventSelector t (Const2 k Bool)
343-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
344-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
345demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
346demux k = Demux (current k)
347 (fan $ attachWith (\k0 k1 -> if k0 == k1
349 else DMap.fromList [Const2 k0 :=> Identity False,
350 Const2 k1 :=> Identity True])
351 (current k) (updated k))
353-- | Select a particular output of the 'Demux'; this is equivalent to (but much
354-- faster than) mapping over the original 'Dynamic' and checking whether it is
355-- equal to the given key.
356demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
358 let e = select (demuxSelector d) (Const2 k)
359 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
361--------------------------------------------------------------------------------
363--------------------------------------------------------------------------------
365--TODO: This whole section is badly in need of cleanup
367-- | A heterogeneous list whose type and length are fixed statically. This is
368-- reproduced from the 'HList' package due to integration issues, and because
369-- very little other functionality from that library is needed.
370data HList (l::[*]) where
372 HCons :: e -> HList l -> HList (e ': l)
376type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
377type instance HRevApp '[] l = l
378type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
380hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
382hRevApp (HCons x l) l' = hRevApp l (HCons x l')
384hReverse :: HList l -> HList (HRevApp l '[])
385hReverse l = hRevApp l HNil
387hBuild :: (HBuild' '[] r) => r
390class HBuild' l r where
391 hBuild' :: HList l -> r
393instance (l' ~ HRevApp l '[])
394 => HBuild' l (HList l') where
397instance HBuild' (a ': l) r
398 => HBuild' l (a->r) where
399 hBuild' l x = hBuild' (HCons x l)
401-- | Like 'HList', but with a functor wrapping each element.
403 FHNil :: FHList f '[]
404 FHCons :: f e -> FHList f l -> FHList f (e ': l)
406instance GEq (HListPtr l) where
407 HHeadPtr `geq` HHeadPtr = Just Refl
408 HHeadPtr `geq` HTailPtr _ = Nothing
409 HTailPtr _ `geq` HHeadPtr = Nothing
410 HTailPtr a `geq` HTailPtr b = a `geq` b
412instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
413 HHeadPtr `gcompare` HHeadPtr = GEQ
414 HHeadPtr `gcompare` HTailPtr _ = GLT
415 HTailPtr _ `gcompare` HHeadPtr = GGT
416 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
418-- | A typed index into a typed heterogeneous list.
419data HListPtr l a where
420 HHeadPtr :: HListPtr (h ': t) h
421 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
423deriving instance Eq (HListPtr l a)
424deriving instance Ord (HListPtr l a)
426fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
427fhlistToDMap = DMap.fromList . go
428 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
431 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
433-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
434-- they must be contiguous and sorted.
435class RebuildSortedHList l where
436 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
437 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
439instance RebuildSortedHList '[] where
440 rebuildSortedFHList l = case l of
442 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
443 rebuildSortedHList l = case l of
445 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
447instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
448 rebuildSortedFHList l = case l of
449 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
450 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
451 rebuildSortedHList l = case l of
452 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
453 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
455dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
456dmapToHList = rebuildSortedHList . DMap.toList
458-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
459-- single 'Dynamic' whose value represents the current values of all of the
461distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
462distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
464-- | Indicates that all elements in a type-level list are applications of the
466class AllAreFunctors (f :: a -> *) (l :: [a]) where
467 type FunctorList f l :: [*]
468 toFHList :: HList (FunctorList f l) -> FHList f l
469 fromFHList :: FHList f l -> HList (FunctorList f l)
471instance AllAreFunctors f '[] where
472 type FunctorList f '[] = '[]
473 toFHList l = case l of
475#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
476 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
478 fromFHList FHNil = HNil
480instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
481 type FunctorList f (h ': t) = f h ': FunctorList f t
482 toFHList l = case l of
483 a `HCons` b -> a `FHCons` toFHList b
484#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
485 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
487 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
489-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
490-- single 'Dynamic' whose value represents all the current values of the input's
491-- constituent 'Dynamic's.
492collectDynPure :: ( RebuildSortedHList (HListElems b)
493 , IsHList a, IsHList b
494 , AllAreFunctors (Dynamic t) (HListElems b)
496 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
497 ) => a -> Dynamic t b
498collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
500-- | Poor man's 'Generic's for product types only.
502 type HListElems a :: [*]
503 toHList :: a -> HList (HListElems a)
504 fromHList :: HList (HListElems a) -> a
506instance IsHList (a, b) where
507 type HListElems (a, b) = [a, b]
508 toHList (a, b) = hBuild a b
509 fromHList l = case l of
510 a `HCons` b `HCons` HNil -> (a, b)
511#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
512 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
515instance IsHList (a, b, c, d) where
516 type HListElems (a, b, c, d) = [a, b, c, d]
517 toHList (a, b, c, d) = hBuild a b c d
518 fromHList l = case l of
519 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
520#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
521 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
524instance IsHList (a, b, c, d, e, f) where
525 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
526 toHList (a, b, c, d, e, f) = hBuild a b c d e f
527 fromHList l = case l of
528 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
529#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
530 _ -> 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