Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Learning Haskell - How to simplify expressions?

Tags:

haskell

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.

like image 474
sevo Avatar asked Aug 28 '14 22:08

sevo


People also ask

How to simplify expressions?

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.

How can I loop an expression in Haskell?

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.

Which expression requires no further simplification?

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..]

What is a lambda expression in Haskell?

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.


1 Answers

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.

like image 66
bheklilr Avatar answered Sep 20 '22 16:09

bheklilr