Is there any way to take the pain out of expression simplification?
For example, given this expression:
(+) <$> a <*> b $ 1
I would love to see a tool that would explain what it means. It's quite laborious for beginners (finding correct instance function definition in sources, checking operator precedence) to simplify expressions with all steps involved:
fmap (+) a <*> b $ 1
See definition in Data.Functor
(.) (+) a <*> b $ 1
See fmap in Control.Monad.Instances
for instance Functor ((->) r)
and so on.
EDIT: To clarify, I'm looking for a way to rewrite expression using actual function definitions so that newcomer could understand the outcome of this expression. How to tell that (<$>) = fmap
here? I don't know how to find a particular instance definition (source) using hoogle
and other tools.
EDIT: Changed incorrect original expression to match following reductions.
How to Simplify Expressions? Simplification of an algebraic expression can be defined as the process of writing an expression in the most efficient and compact form without affecting the value of the original expression. The process entails collecting like terms, which implies adding or subtracting terms in an expression.
Haskell does not provide any facility of looping any expression for more than once. Instead, Haskell wants you to break your entire functionality into a collection of different functions and use recursion technique to implement your functionality.
The expression inside the brackets requires no further simplification. Are there any exponents? Yes. Simplify exponents. Is there any multiplication or division? Yes. Multiply. Is there any addition or subtraction? Yes. Add. Add. Box 1: Enter your answer as an integer or decimal number. Examples: 3, -4, 5.5172 [more..]
We sometimes have to write a function that is going to be used only once, throughout the entire lifespan of an application. To deal with this kind of situations, Haskell developers use another anonymous block known as lambda expression or lambda function. A function without having a definition is called a lambda function.
I find that the easy way is to use typed holes available in GHCi 7.8:
> (*10) <$> _a $ 1
Found hole ‘_a’ with type: s0 -> b
Where: ‘s0’ is an ambiguous type variable
‘b’ is a rigid type variable bound by
the inferred type of it :: b at <interactive>:4:1
Relevant bindings include it :: b (bound at <interactive>:4:1)
In the second argument of ‘(<$>)’, namely ‘_a’
In the expression: (* 10) <$> _a
In the expression: (* 10) <$> _a $ 1
So this tells me that a :: s0 -> b
. Next is to figure out the order of operators:
> :i (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
infixl 4 <$>
> :i ($)
($) :: (a -> b) -> a -> b
infixr 0 $
So this says that $
is highly right-associative, and given it's type we see that it's first argument must be a function, so a
must be a function (double confirmation). This means that (*10) <$> a $ 1
is the same as ((*10) <$> a) $ 1
, so we'll focus on (*10) <$> a
first.
> :t ((*10) <$>)
((*10) <$>) :: (Num a, Functor f) => f a -> f a
> :t (<$> _a)
Found hole ‘_a’ with type: f a
Where: ‘a’ is a rigid type variable bound by
the inferred type of it :: (a -> b) -> f b at Top level
‘f’ is a rigid type variable bound by
the inferred type of it :: (a -> b) -> f b at Top level
In the second argument of ‘(<$>)’, namely ‘_a’
In the expression: (<$> _a)
So we need a
to be a functor. What are available instances?
> :i Functor
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
-- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘Data.Maybe’
instance Functor (Either a) -- Defined in ‘Data.Either’
instance Functor ZipList -- Defined in ‘Control.Applicative’
instance Monad m => Functor (WrappedMonad m)
-- Defined in ‘Control.Applicative’
instance Control.Arrow.Arrow a => Functor (WrappedArrow a b)
-- Defined in ‘Control.Applicative’
instance Functor (Const m) -- Defined in ‘Control.Applicative’
instance Functor [] -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((,) a) -- Defined in ‘GHC.Base’
So (->) r
happens to be one, which is awesome because we know a
has to be a function. From the Num
constraint, we can determine that r
must be the same as Num a => a
. This means that (*10) <$> a :: Num a => a -> a
. From that we then apply 1
to it, and we'd get (*10) <$> a $ 1 :: Num a
, where a
is some unknown function.
All this is discoverable using GHCi using :t
and :i
, along with typed holes. Sure, there's a fair number of steps involved, but it never fails when you're trying to break down a complex expression, just look at the types of different sub-expressions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With