Applicative functors other than monads and ZipList?

前端 未结 7 865
小鲜肉
小鲜肉 2020-12-31 07:36

Two well-known examples of applicatives are monads and ziplists. Are there any other examples?

7条回答
  •  悲哀的现实
    2020-12-31 08:39

    I recently defined an Applicative instance for a newtype on top of (,,,), a "quad". (The standard library defines an instance for (,), but not (,,,). That's OK, as the standard implementation has different semantics than what I was after.)

    The background is; I am parsing some old data, and the date format in the data is ambiguous. Each date in the data can be parsed into four possibilities, stored in the quad. I then want to validate each date in the quad to eliminate semantically invalid dates. (There are no months with 32 days, there is no month 34, there is no 5th quarter, etc.) Finally, I want to take each date in the dataset, and reduce the entire set to a quad representing which date formats are valid for the entire set. Then, I choose the best format out of those options, and assume that's what the date format of the dataset is.

    This entire operation is very easy to express as applicative operations on the quad structure.

    Here's the basic shape of the code:

    My new type:

    newtype DQ a = DQ (a, a, a, a) -- date quad
                 deriving ...
    
    instance Functor DQ where
       g `fmap` f = pure g <*> f
    
    instance Applicative DQ where
       pure x  = DQ (x, x, x, x)
       DQ (g, h, i, j) <*> DQ (a, b, c, d) = DQ (g a, h b, i c, j d)
    

    Some prerequisite "pure" functions:

    parseDateInt :: Int -> DQ Date
    validateDate :: Date -> Bool
    extractBestDate :: DQ Date -> DQ Bool -> Date
    

    So once we have the quad of parsed dates (from parseDateInt), we need to validate them:

    validateDates :: DQ Date -> DQ Bool
    validateDates = (validateDate <$>)
    

    (This is only a Functor so far, but you could also write (pure validateDate <*>).

    It's also worth noting the symmetry between validation of a single element, and validating each element of the set -- to validate one, you might write validateDate $ date; to validate a set, you write validateDate <$> dates. This is why fmap is written as <$>, it's function application to a functor.)

    The step after this is to take a set of valid parses and fold that into a final result:

    intuitDateType :: [DQ Bool] -> DQ Bool
    intuitDateType dates = foldl1 (liftA2 (&&)) dates
    

    So now you can go from the [Int] in the data file to a DQ Bool representing the possibly-valid date representations for a dataset. (And from there, associate each data point with a real date object, instead of the flaky Int that was supplied.)

    So anyway, this post has gotten a bit long, but the idea is that an Applicative instance allowed me to solve my problem in about 3 lines of code. My problem domain was repeatedly applying functions to data in a container, which is what an Applicative functor does. There is no join operation on this data, so a Monad instance would not make much sense.

提交回复
热议问题