2

I finally got bored enough in quarantine to start learning Haskell today and I'm really enjoying myself, I really love the aesthetics of the language. Hopefully this question doesn't inspire any hate if it has already been posted here before, etc. It's very simple, but I'm an absolute beginner.

I've been trying to understand how to do some simple things elegantly (or at least in a so-called "point free" way) with the language and came upon the problem of how to describe in a clean way the procedure of taking a function of two variables and restricting it to the graph of a function between the two variables. E.g. how to take a function of two variables of the same type and restrict it along the diagonal to obtain a function of one variable with the same type and the same type of outputs, or any similar problem (a bit like trying currying on the wrong side of the hom, if you like).

I was messing around today with concise ways of doing this but eventually gave up and implemented the following for later use:

compFtn :: (a -> b -> c) -> (a -> b) -> a -> c

compFtn f g a = f a (g(a)) 

which does what I wanted (as would, say, decurrying f and doing this in a really ugly fashion). However I have a feeling that there must be a way more "point free"/doctrinaire solution to this very basic operation, so I'm here asking around to learn some style from y'all. Cheers.

  • 3
    You should look into the Applicatives and especially the `<*>` operator. – Redu May 20 '20 at 07:15
  • 2
    @Redu's proposal is quite nice actually. It'll be a headache if you try, but it'll be a good exercise. Nevertheless, don't focus too much on point free, every function in haskell can be written in point-free style and most of the times it is completely obscure. I'd say that an elegant way to express your function is `comptFtn f g a = a \`f\` g a` or `comptFtn f g a = f a $ g a` – lsmor May 20 '20 at 07:35
  • Perhaps try the Code Review stack exchange site? –  May 20 '20 at 14:07
  • @MichaelLitchard I don't really know what the protocol is, I figured this was a reasonably general conceptual question about Haskell and I didn't really have questions about any specific snippet of code (I only included some to show that I had thought about the problem and wasn't being lazy/disingenuous). My question seems to be basically at the same level as https://stackoverflow.com/questions/11810889/functions-as-applicative-functors-haskell-lyah so in retrospect it seems reasonable to put it here, but perhaps standards have evolved. – HaskellLearner May 20 '20 at 15:29
  • @Ismor the reason I mentioned that I wanted a "point free" method is just because as the question is about a very fundamental pullback operation on morphisms, there must be some operator defined purely on functions which gives the "conceptually correct" solution to this problem. – HaskellLearner May 20 '20 at 16:03

1 Answers1

2

A function is a functor, an applicative and a monad. We may consider functions as a context with a contained value revealed once applied.

So for a function type Functor instance would be the composition (.) operator. Why? Lets say we have a function (+1) which says when you apply me a Num a class value i will increment it by 1. So lets implement fmap.

fmap f g = f . g -- <$> == (.)

obviously we are applying f to the return value of g.

So how can we make a function an instance of Applicative? It must contain another function in its context. Basically it should return a function when applied. This means (a -> b -> c) at least. So here you go.

(a -> b -> c) <*> (a -> b) is in fact once a is applied (a -> b -> c) becomes (b -> c) and (a -> b) becomes b and (b -> c) gets applied with the b. So yes what you are looking for is <*> operator.

Redu
  • 25,060
  • 6
  • 56
  • 76
  • 2
    Excellent! Sorry it took me a while to verify your solution as I hadn't read about how functors are used in Haskell yet. But this seems to make perfect sense, I suppose my function above is literally the <*> operator for a special type of applicative, as you say. Thanks so much for your help! This was definitely an issue where I simply lacked a great deal of Haskell culture. I suppose I have quite a bit of reading/coding to do now if I hope to understand what applicatives are trying to accomplish conceptually... – HaskellLearner May 20 '20 at 08:20
  • Applicatives are there to let you apply functions within the context they are already in. A simple example would be `Just (+1)`. Now the `(+1)` function is in Maybe monad (the context of being unsure about the outcome). If we do like `Just (+1) <*> Just 10` we get `Just 11` without even stripping of the function off the context. Why is this important? Because since you are in Maybe (unsure about the outcome) `Just 10` could be `Nothing` depending on how it was calculated in the first place (i.e. the calculation failed and you get `Nothing`) then `Just (+1) <*> Nothing` gives you `Nothing`. :) – Redu May 20 '20 at 16:09
  • This is a great explanation! Thank you very much for your help, this all seemed so needlessly opaque a day ago but the fog is beginning to clear after reading up a bit on the api and experimenting a bit with functors and applicatives myself. – HaskellLearner May 20 '20 at 21:35