3 ms·
Could someone explain 'applicative'? To me it seems like throwing some magic syntax at a simple usecase to make it more terse and less readable - but maybe I mi
by m_mueller 11y ago
Could someone explain 'applicative'? To me it seems like throwing some magic syntax at a simple usecase to make it more terse and less readable - but maybe I misunderstand and it's something every Haskell programmer knows.
- nbouscal 11y agoApplicatives are definitely something that every Haskell programmer knows. I would expect everyone on the team to understand the recommended new version, but it's less clear whether it is actually an improvement. There is a proposal to allow applicatives to use the same syntax as monads (the original code, before the change), which would render the question moot: https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo
- purescript 11y agoAn Applicative functor is a type constructor which lets you lift functions of arbitrary numbers of arguments to functions whose arguments are wrapped with that type constructor. Concretely, if you think about promises in JavaScript, for example, if you have a function A -> B -> C -> D, say, and you have three promises of types Promise A, Promise B and Promise C, you can construct a Promise of type Promise D by running the three in parallel, and applying your three-argument function when they are all done. So you've turned a function of type A -> B -> C -> D into one of type Promise A -> Promise B -> Promise C -> Promise D. If you can do that sensibly for any number of function arguments, you have an Applicative. ("sensibly" here means that there are type class laws which have to hold) Every Monad is also an Applicative functor, since you could use do notation to compose your promises instead, but there are other interesting Applicatives which do not come from Monads.
- spopejoy 11y agoIn most cases (like the example), it simply allows you to call "effectful" code (often monadic, IO etc) to populate arguments of a function. My favorite example is "I want to grab three web pages concurrently and return them in a single triple." To the haters who think this is just golf, just TRY to write this as beautifully in any other language. (page1, page2, page3) <- runConcurrently $ (,,) <$> Concurrently (getURL "url1") <*> Concurrently (getURL "url2") <*> Concurrently (getURL "url3") (taken from the excellent haddocks for Control.Current.Async https://hackage.haskell.org/package/async-2.0.1.4/docs/Control-Concurrent-Async.html#t:Concurrently https://hackage.haskell.org/package/async-2.0.1.4/docs/Contr...)
- doublec 11y agoAlice ML: val (page1, page2, page3) = (spawn getURL "url1", spawn getURL "url2", spawn getURL "url3") The requests are concurrent. Spawn returns a future and 'page1', etc are futures immediately. When the value of 'page1', etc is requested later the requesting process either blocks until the getURL for that value is complete, or transforms into the value implicitly if it's already done. I think it's just as nice as the Haskell example. I do agree that Haskell is a great language though.
- purescript 11y agoI don't know Alice ML but one advantage of using abstractions like Applicative is that you can write code which is polymorphic in the particular applicative you choose. So, you can mock your concurrent requests using the Identity or ZipList applicative, and use the type class laws to prove things which are true about both. I'm guessing, but it looks like the spawn syntax is built-in here. Not that it's not as elegant as the Concurrent version, but likely not as powerful from an abstraction point of view.
- doublec 11y agoCorrect, spawn is built in in Alice ML. Haskell wins on all the ways it can be used beyond built in functionality compared to it.
- deleted 11y ago[deleted]
- dllthomas 11y agoThere's no actual magic syntax, just some ordinary operators. (<$>) is infix "fmap" (<*>) is infix "ap" The "fmap" function lets you apply something of the form (a -> b) to a value of the form (m a), to get something of the form (m b). Specialized to lists, it's the familiar "map" function, but there are a lot of other things it can apply to. The "ap" function lets you apply a "wrapped" function to a "wrapped" value - "apply this list of functions to that list of values". The way these combine, along with currying, means you get a well known pattern for applying a many argument function to many wrapped values: <$> before the first argument <*> before every remaining argument It works out like this: let add3 :: Integer -> Integer -> Integer -> Integer add3 x y z = x + y + z (add3 5) :: Integer -> Integer -> Integer (add3 <$> Just 5) :: Maybe (Integer -> Integer -> Integer) (add3 <$> Just 5 <*> Just 3) :: Maybe (Integer -> Integer) (add3 <$> Just 5 <*> Just 3 <*> Just 9) :: Maybe Integer