I'm writing a lot of code like the following:
popStack groupTail
|> andThen
(\( parentGroup, parentTail ) ->
addChild currentGroup sibling
|> andThen
(\updatedParent ->
case sibling of
SingleExercise _ ->
workHelp siblingIndent (updatedParent :: parentTail)
WorkGroup _ _ ->
workHelp siblingIndent (sibling :: (updatedParent :: parentTail))
)
)
It feels a lot like callback hell with all the nested andThen calls, and I was wondering if there are idiomatic ways to use different kinds of function application to avoid all the nesting.
#Reactormonk provided a useful link, but Elm is not Haskell. Otherwise, we could use Maybe monad and syntactic sugar provided by do-notation. Something like:
do
(parentGroup, parentTail) <- popStack groupTail
updatedParent <- addChild currentGroup sibling
case sibling of
SingleExercise _ ->
workHelp siblingIndent (updatedParent : parentTail)
WorkGroup _ _ ->
workHelp siblingIndent (sibling : (updatedParent : parentTail))
But in Elm I'd end up with moving logic into separate functions:
let
workHelpToSibling sibling ( parentTail, updatedParent ) =
case sibling of
SingleExercise ->
workHelp siblingIndent (updatedParent :: parentTail)
WorkGroup ->
workHelp siblingIndent (sibling :: (updatedParent :: parentTail))
addChildTo currentGroup sibling ( parentGroup, parentTail ) =
addChild currentGroup sibling
|> Maybe.map (\updatedParent -> ( parentTail, updatedParent ))
in
popStack groupTail
|> andThen (addChildTo currentGroup sibling)
|> andThen (workHelpToSibling sibling)
Since you have currentGroup and sibling accessible on a higher level, this code can be refactored by reducing the arity of the functions (and not only by this).
I just wanted to point out the idea of minimizing the level of indentation, which indeed reminds callback hell.
Related
A common pattern in functional programming languages with a sufficiently advanced type system to to have a type of "heterogeneous lists". For instance, given a list defined as:
data List a = Nil | Cons a (List a)
(Note: For concreteness, I will use Idris in this question, but this could also be answered in Haskell (with the right extensions), Agda, etc...)
We can define HList:
data HList : List a -> Type where
Nil : HList []
(::) : a -> HList as -> HList (a :: as)
This is a list which holds a different type (specified by the type-level List a) at each "position" of the List data type. This made me wonder: Can we generalize this construction? For instance, given a simple tree-like structure:
data Tree a = Branch a [Tree a]
Does it make sense to define a heterogenous tree?
where HTree : Tree a -> Type where
...
More generally in a dependently-typed language, is it possible to define a general construction:
data Hetero : (f : Type -> Type) -> f a -> Type where
....
that takes a data type of kind Type -> Type and returns the "heterogeneous container" of shape f? Has anyone made use of this construction before if possible?
We can talk about the shape of any functor using map and propositional equality. In Idris 2:
Hetero : (f : Type -> Type) -> Functor f => f Type -> Type
Hetero f tys = (x : f (A : Type ** A) ** map fst x = tys)
The type (A : Type ** A) is the type of non-empty types, in other words, values of arbitrary type. We get heterogeneous collections by putting arbitrarily typed values into functors, then constraining the types elementwise to particular types.
Some examples:
ex1 : Hetero List [Bool, Nat, Bool]
ex1 = ([(_ ** True), (_ ** 10), (_ ** False)] ** Refl)
data Tree : Type -> Type where
Leaf : a -> Tree a
Node : Tree a -> Tree a -> Tree a
Functor Tree where
map f (Leaf a) = Leaf (f a)
map f (Node l r) = Node (map f l) (map f r)
ex2 : Hetero Tree (Node (Leaf Bool) (Leaf Nat))
ex2 = (Node (Leaf (_ ** False)) (Leaf (_ ** 10)) ** Refl)
I'm trying to figure out why the type-checker rejects this:
module Test
import Data.Vect
import Data.HVect
data Tree = Leaf | Branch (Vect n Tree)
data Garble : Tree -> Type where
Farble : Garble Leaf
Marble : HVect (map Garble children) -> Garble (Branch children)
Eq (Garble tree) where
Farble == Farble = True
(Marble comps1) == (Marble comps2) = comps1 == comps2 -- breaks here
_ == _ = False
This complains because it can't find an instance for Eq (HVect (map Garble children)) in the Marble case of the Eq instance. I suspect that's because the map expression isn't reduced prior to interface resolution, or that polymorphic recursion is somehow responsible for this, e.g. that there's no instance available for Eq (Garble tree') where tree' /= tree...
Well, enlighten me :)
I'm trying to implement a simple algebraic structures hierarchy using Idris interfaces. The code is as follows:
module AlgebraicStructures
-- definition of some algebraic structures in terms of type classes
%access public export
Associative : {a : Type} -> (a -> a -> a) -> Type
Associative {a} op = (x : a) ->
(y : a) ->
(z : a) ->
(op x (op y z)) = (op (op x y) z)
Identity : {a : Type} -> (a -> a -> a) -> a -> Type
Identity op v = ((x : a) -> (op x v) = x,
(x : a) -> (op v x) = x)
Commutative : {a : Type} -> (a -> a -> a) -> Type
Commutative {a} op = (x : a) ->
(y : a) ->
(op x y) = (op y x)
infixl 4 <**>
interface IsMonoid a where
empty : a
(<**>) : a -> a -> a
assoc : Associative (<**>)
ident : Identity (<**>) empty
interface IsMonoid a => IsCommutativeMonoid a where
comm : Commutative (<**>)
But, Idris is giving this strange error message:
When checking type of constructor of AlgebraicStructures.IsCommutativeMonoid:
Can't find implementation for IsMonoid a
I believe that Idris interfaces works like Haskell's type classes. In Haskell, it should work. Am I doing something silly?
I believe it may be complaining because I don't know that there's anything that constrains the a in the expression Commutative (<**>) - so it doesn't know that you can invoke <**> on that type.
Explicitly specifying the a seems to work for me - Commutative {a} (<**>) - I hope that that means that the a from the interface signature is in scope and available for explicitly passing to other types.
I am getting an unsolved metavariable for foo in the code below:
namespace Funs
data Funs : Type -> Type where
Nil : Funs a
(::) : {b : Type} -> (a -> List b) -> Funs (List a) -> Funs (List a)
data FunPtr : Funs a -> Type -> Type where
here : FunPtr ((::) {b} _ bs) b
there : FunPtr bs b -> FunPtr (_ :: bs) b
total foo : FunPtr [] b -> Void
How do I convince Idris that foo has no valid patterns to match on?
I've tried adding
foo f = ?foo
and then doing a case split in Emacs on f (just to see what might come up), but that just removes the line, leaving foo as an unsolved meta.
It turns out all I need to do is enumerate all possible patterns for foo's argument, and then Idris is able to figure out, one by one, that they are un-unifyable with foo's type:
foo : FunPtr [] b -> Void
foo here impossible
foo (there _) impossible
I have a type class Atomic, which defines functions for converting certain types to/from a wrapper value (Atom). I'd like to define a QuickCheck property which states: "for all instances of Atomic, any value may be stored and retrieved safely". The property looks like this:
class Atomic a where
toAtom :: a -> Atom
fromAtom :: Atom -> Maybe a
prop_AtomIdentity x = fromAtom (toAtom x) == Just x
However, if I just try to run that property through QuickCheck, it just picks one instance (Bool) and tests it. I'm currently working around that by defining type signatures for each supported atomic type in the test list, but this is verbose and error-prone:
containerTests =
[ run (prop_AtomIdentity :: Bool -> Bool)
, run (prop_AtomIdentity :: Word8 -> Bool)
, run (prop_AtomIdentity :: String -> Bool)
{- etc -} ]
I'm trying to define a function which will do this automatically:
forallAtoms :: (Atomic a, Show a) => (a -> Bool) -> [TestOptions -> IO TestResult]
forallAtoms x =
[ run (x :: Bool -> Bool)
, run (x :: Word8 -> Bool)
, run (x :: String -> Bool)
{- etc -} ]
containerTests = forallAtoms prop_AtomIdentity
But it fails with a typecheck error:
Tests/Containers.hs:33:0:
Couldn't match expected type `Word8' against inferred type `String'
In the first argument of `run', namely `(x :: Word8 -> Bool)'
In the expression: run (x :: Word8 -> Bool)
In the expression:
[run (x :: Bool -> Bool), run (x :: Word8 -> Bool),
run (x :: String -> Bool)]
Is there a better way to test a QC property against multiple types? If not, can forallAtoms be made to work or is that not supported by the type system?
I cannot compile your code, so ... blind shot:
try
forallAtoms :: (forall a. (Atomic a, Show a) => a -> Bool) -> [TestOptions -> IO TestResult]
as a type signature. This needs the -XRankNTypes language extension.
The problem you have, as I see it, is that GHC tries to find one type to insert for a in x :: (a -> Bool) for the entire function scope, but you already give three different there.