Data of the high genus

 3r3-3589. 3r3-31. Yes, yes, you have not dreamed, and you have not misheard - it is a high kind. A gender (3r33535. Kind 3r33565.) Is a term in category theory, meaning essentially the type of type[данных]. 3r? 3572.  3r3-3589. But first, some lyrics. 3r? 3572.  3r3-3589. On Habré several articles appeared, which described in detail the method of validation of data in functional languages. 3r? 3572.  3r3-3589. This article - my five cents in this HYIP. We will look at data validation in Haskell. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. 3r33333. Validation Type
3r? 3572.  3r3-3589. An example of a validation technique using the validation type was considered previously: 3r33572.  3r3-3589. 3r33536. 3r33537. type EmailContactInfo = String
type PostalContactInfo = String
3r3-3589. data ContactInfo = EmailOnly EmailContactInfo | 3r3-3589. PostOnly PostalContactInfo | 3r3-3589. EmailAndPost (EmailContactInfo, PostalContactInfo)
3r3-3589. data Person = Person
{pName :: String,
, pContactInfo :: ContactInfo, 3r33553.} 3r33548. 3r3-3549. 3r? 3572.  3r3-3589. Using this method is simply impossible to create incorrect data. However, despite the fact that such validation is very easy to create and read, using it forces you to write a lot of routine and make many changes to the code. So, the use of such a method is limited only for really important data. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. 3r33333. Validation data of the high genus
3r? 3572.  3r3-3589. Data of the high genus  3r3-3589. 3r33536. 3r33537. data Person = Person
{pName :: String
, pAge :: Int
} 3r33548. 3r3-3549. 3r? 3572.  3r3-3589. And we will validate the data only if all the fields in the record are valid. 3r? 3572.  3r3-3589. Since Haskell is more functional than most functional languages, you can easily get rid of most of the routines. 3r? 3572.  3r3-3589. Here it is possible and therefore this method is widely used among authors of Haskell libraries. 3r? 3572.  3r3-3589. 3r366. 3r33576. 3r? 3572.  3r3-3589. For purposes of discussion, let's imagine that we want the user to fill in the identity data via a web form or something else. In other words, it is possible that they may spoil the filling of some of the information without necessarily canceling the rest of the data structure. If they have successfully completed the entire structure, we would like to have a filled out 3r-3564. Person [/b] . 3r? 3572.  3r3-3589. One of the modeling methods is to use the second data type: 3r33572.  3r3-3589. 3r33536. 3r33537. data MaybePerson = MaybePerson
{mpName :: Maybe String
, mpAge :: Maybe Int
} 3r33548. 3r3-3549. 3r? 3572.  3r3-3589. where, let me remind you use the optional type:
 3r3-3589. 3r33536. 3r33537. - already in Prelude
data Maybe a = Nothing | Just a 3r33548. 3r3-3549. 3r? 3572.  3r3-3589. Hence, the validation function is quite simple: 3r33572.  3r3-3589. 3r33536. 3r33537. validate :: MaybePerson -> Maybe Person
validate (MaybePerson name age) = 3r33595. Person 3r33500. name <*> age 3r3-3549. 3r? 3572.  3r3-3589.
A little more detail about the functions (3r33500.) And (3r3-33460.) 3r33565. 3r3111. Function 3r3-3564. (3r33500.) 3r33565. Is only an infix synonym for Functor 3r353564. fmap [/b] 3r? 3572.  3r3-3589. 3r33536. 3r33537. - already in Prelude
fmap :: Functor f => (a -> b) -> f a -> f b
3r3-3589. ( <$> ) :: Functor f => (a -> b) -> f a -> f b
( <$> ) = Fmap 3r3-3549. 3r? 3572.  3r3-3589. And 3r33564. (3r33460.) 3r33565. Is a function of applying the applicative functor of 3-33572.  3r3-3589. 3r33536. 3r33537. - already in Prelude
( <*> ) :: Applicative f => f (a -> b) -> f a -> f b 3r3-3549. 3r? 3572.  3r3-3589. And for the optional type, these functions have the following definition of 3r33572.  3r3-3589. 3r33536. 3r33537. - already in Prelude
( <$> ) :: (a -> b) -> Maybe a -> Maybe b
_ 3r33500. Nothing = Nothing
f 3r3–3500. (Just a) = Just (f a) 3r3-3589. 3r3-3589. ( <*> ) :: Maybe (a -> b) -> Maybe a -> Maybe b
(Just f) <*> m = f 3r???. m
Nothing <*> _ = Nothing
3r33548. 3r3-3549. 3r? 3572.  3r3-3589. 3r33585. 3r33585. 3r? 3572.  3r3-3589. Our validation works, but it is annoying to write by hand an additional routine code, since this is done completely mechanically. Moreover, the duplication of these efforts means that we will need to use our brains in the future to make sure that all three definitions remain synchronized. Would it be great if the compiler could handle it? 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. SURPRISE! HE CAN! We will help the high race! 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. In Haskell there is such a thing as a genus, it is also 3r-3664. kind [/b] and the simplest and most accurate explanation is that the genus is a type of type[данных]. The most widely used genus is 3r3–3564. * 3r3655. , which can be called the "end" 3r33572.  3r3-3589. 3r33536. 3r33537. ghci>: k Int
Int :: * 3r38989. 3r3-3589. ghci>: k String
String :: * 3r3589. 3r3-3589. ghci>: k Maybe Int
Maybe Int :: * 3r38989. 3r3-3589. ghci>: k Maybe String
Maybe String :: *
3r3-3589. ghci>: k[Int]3r3-3589.[Int]:: * 3r33548. 3r3-3549. 3r? 3572.  3r3-3589. And what kind of Maybe ? 3r? 3572.  3r3-3589. 3r33536. 3r33537. ghci>: k Maybe
Maybe :: * -> *
3r3-3589. ghci>: k[]3r3-3589.[]:: * -> * 3r3-3549. 3r? 3572.  3r3-3589. This is an example of a high kind. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. Please note that we can describe as Person , and 3r36464. MaybePerson [/b] the following single data of a high genus: 3r3r722.  3r3-3589. 3r33536. 3r33537. data Person 'f = Person
{pName :: f String
, pAge :: f Int
} 3r33548. 3r3-3549. 3r? 3572.  3r3-3589. Here we parameterize 3r33535. Person ' [/b] over something r3r3564. f 3r3-3565. (genus 3r33564. * -> * 3r36565.), which allows us to do the following to use the original types: 3r33572.  3r3-3589. 3r33536. 3r33537. type Person = Person 'Identity
type MaybePerson = Person 'Maybe 3r3-3549. 3r? 3572.  3r3-3589. Here we use the simple wrapper type of Identity
 3r3-3589. 3r33536. 3r33537. - already in Prelude
newtype Identity a = Identity {runIdentity :: a} 3r3-3549. 3r? 3572.  3r3-3589. Although it works, it is a bit annoying in the case of 3r33535. Person [/b] , since now all our data is wrapped inside 3r33535. Identity [/b] :
 3r3-3589. 3r33536. 3r33537. ghci>: t pName @Identity
pName :: Person -> Identity String
3r3-3589. ghci>: t runIdentity. pName
runIdentity. pName :: Person -> String 3r3-3549. 3r? 3572.  3r3-3589. We can eliminate this annoyance trivially, after which we consider why exactly such a definition of 3r-3564. Person ' [/b] really helpful. To get rid of identifiers, we can use the family of types (function at the type level), which erases them:
 3r3-3589. 3r33536. 3r33537. {- # LANGUAGE TypeFamilies # -}
3r3-3589. - "Higher-Kinded Data"
type family HKD f a where
HKD Identity a = a
HKD f a = f a
3r3-3589. data Person 'f = Person
{pName :: HKD f String
, pAge :: HKD f Int
} deriving (Generic) 3r3-3549. 3r? 3572.  3r3-3589. Conclusion 3r33535. Generic 3r3-3565. we need for the 2nd part of the article. 3r? 3572.  3r3-3589. Use of family types 3r33535. HKD 3r3-3565. means that GHC automatically erases any wrappers 3r3353564. Identity [/b] in our submissions:
 3r3-3589. 3r33536. 3r33537. ghci>: t pName @Identity
pName :: Person -> String
3r3-3589. ghci>: t pName @Maybe
pName :: Person -> Maybe String 3r3-3549. 3r? 3572.  3r3-3589. and this is exactly the version Person High kind can be used best as a replacement replacement for our original one. 3r? 3572.  3r3-3589. The obvious question is that we bought ourselves with all this work done. Let's go back to the validation wording to help us answer this question. 3r? 3572.  3r3-3589. We can now rewrite it with the help of our new technology:
 3r3-3589. 3r33536. 3r33537. validate :: Person 'Maybe -> Maybe Person
validate (Person name age) = 3r33589. Person 3r33500. name <*> age 3r3-3549. 3r? 3572.  3r3-3589. Not a very interesting change? But the intrigue is how little you need to change. As you can see, only our type and pattern match our initial implementation. What is neat here is that we have now consolidated 3r3353564. Person [/b] and 3r33564. MaybePerson [/b] in the same representation, and therefore they are no longer related only in a nominal sense. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. 3r33333. Generics and more general function of validation
3r? 3572.  3r3-3589. The current version of the validation function must be written for each new data type, even though the code is quite routine. 3r? 3572.  3r3-3589. We can write a validation version that will work for any higher data type. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. It would be possible to use Template Haskel (3r33535. TemplateHaskell 3r33535.), But it generates code and is used only in extreme cases. We will not. 3r? 3572.  3r3-3589. The secret is to turn to r3r3564. GHC.Generics [/b] . If you are unfamiliar with the library, it provides an isomorphism from the regular Haskell data type into a general representation that can be structurally controlled by a smart programmer (that is: us.) By providing code for changing constant types, products and coproducts, we can force GHC write code for us independent of the type. This is a very neat technique that will tickle your toes, if you have not seen it before. 3r? 3572.  3r3-3589. We finally want to get something like: 3r33535.  3r3-3589. 3r33536. 3r33537. validate :: _ => d Maybe -> Maybe (d Identity) 3r3-3549. 3r? 3572.  3r3-3589. From the point of view of 3r33564. Generics 3r33535. any type can most generally be divided into several constructions: 3r33535.  3r3-3589. 3r33536. 3r33537. - undefined data, lifted version of Empty
data v1 p 3r3-3589. - Unit: used for constructors without lifts, version
data U1 p = U1
- a container for a c, Constants, additional parameters and recursion of kind *
newtype K1 i c p = K1 {unK1 :: c}
- a wrapper, Meta-information (constructor names, etc.)
newtype M1 i t f p = M1 {unM1 :: f p}
3r3-3589. - Sums: encode choice between constructors, lifted version of Either
data (: + :) f g p = L1 (f p) | R1 (g p) 3r3-3899. - Products: encode multiple arguments to constructors, lifted version of (,)
data (: * :) f g p = (f p): *: (g p) 3r3-3548. 3r3-3549. 3r? 3572.  3r3-3589. That is, there may be non-liberalized structures, non-argument structures, constant structures, meta-informational (constructors, etc.). As well as the union of structures - the total or association of the type of OR-OR and animated, they are also kortezhnye associations or records. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. First we need to determine the class that will be the workhorse of our transformation. By experience, this is always the most difficult part - the types of these generalized transformations are extremely abstract and, in my opinion, very difficult to reason about. Let's use:
 3r3-3589. 3r33536. 3r33537. {- # LANGUAGE MultiParamTypeClasses # -}
3r3-3589. class GValidate i o where
gvalidate :: i p -> Maybe (o p) 3r3-3549. 3r? 3572.  3r3-3589. You can use the “soft and slow” rules for reasoning about how your class type should look, but in general you will need both an input parameter and an output parameter. They both should be of the genus * -> * 3r3565. and then transfer this existentialized 3r33564. p 3r3-3565. due to dark, unholy reasons that are not known to mankind. Then, using the small checklist, go through to help wrap your head around thisof the hellish landscape, which we will go round later on. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. In any case, our class is already in our hands, now we just need to write out copies of our class for different types of 3r36464. GHC.Generic 3r3-3565. . We can start with the base case, which we should be able to check, namely 3r-3564. Maybe k 3r33565. :
 3r3-3589. 3r33536. 3r33537. {- # LANGUAGE FlexibleInstances # -}
{- # LANGUAGE TypeOperators # -}
3r3-3589. Instance GValidate (K1 a (Maybe k)) (K1 a k) where
- gvalidate :: K1 a (Maybe k) -> Maybe (K1 a k)
gvalidate (K1 k) = K1 3r33500. k 3r3-3589. {- # INLINE gvalidate # -} 3r3-3549. 3r? 3572.  3r3-3589. 3r33535. K1 3r36565. is a "constant type", which means that this is where our structural recursion ends. In the example with our Person ' it will be 3r33564. pName :: HKD f String
. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. In most cases, when you have a base case, the rest are simply mechanically determined instances for other types. If you do not need access to metadata about the original type anywhere, these instances will almost always be trivial homomorphisms. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. We can start with multiplicative structures - if we have 3r3353564. GValidate i o [/b] and 3r33564. GValidate i 'o' [/b] , we should be able to run them in parallel: 3r33572.  3r3-3589. 3r33536. 3r33537. instance (GValidate i o, GValidate i 'o')
=> GValidate (i: *: i ') (o: *: o') where
gvalidate (l: *: r) = (: * :)
3r?500. gvalidate l
3r33460. gvalidate r
{- # INLINE gvalidate # -} 3r3-3549. 3r? 3572.  3r3-3589. If 3r3-3564. K1 3r36565. refers directly to our selectors. Person ' , (: * :) roughly corresponds to the syntax with a comma, with which we separate our fields in the record. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. We can define a similar instance of GValidate for coproducts or summary structures (the corresponding values ​​are separated by 3r33564. | 3r33565. in the data definition): 3r33572.  3r3-3589. 3r33536. 3r33537. instance (GValidate i o, GValidate i 'o')
=> GValidate (i: +: i ') (o: +: o') where
gvalidate (L1 l) = L1 3r3–3500. gvalidate l
gvalidate (R1 r) = R1 3r33500. gvalidate r
{- # INLINE gvalidate # -} 3r3-3549. 3r? 3572.  3r3-3589. In addition, since we do not care about finding metadata, we can simply determine 3r-3564. GValidate i o [/b] above the metadata constructor:
 3r3-3589. 3r33536. 3r33537. Instance GValidate i
=> GValidate (M1 _a _b i) (M1 _a '_b' o) where
gvalidate (M1 x) = M1 3r33500. gvalidate x
{- # INLINE gvalidate # -} 3r3-3549. 3r? 3572.  3r3-3589. Now there are uninteresting structures for a complete description. We will provide them with the following trivial copies for non-residential types (3r33564. V1 3r33565.) And for designers without any parameters (3r33535. U1 3r36565.): 3r33572.  3r3-3589. 3r33536. 3r33537. Instance GValidate V1 V1 where
gvalidate = undefined
{- # INLINE gvalidate # -}
3r3-3589. Instance GValidate U1 U1 where
gvalidate U1 = Just U1
{- # INLINE gvalidate # -} 3r3-3549. 3r? 3572.  3r3-3589. Using 3r33535. undefined [/b] it is safe here because it can only be called with a value of 3r33535. V1 3r???. . Fortunately for us, V1 3r???. uninhabited and uninitialized, so this can never happen, so we are morally right in our use of 3r-3664. undefined . 3r? 3572.  3r3-3589. Without further ado, now that we have this whole mechanism, we can finally write a non-generic version of validation: 3r3r722.  3r3-3589. 3r33536. 3r33537. {- # LANGUAGE FlexibleContexts # -}
3r3-3589. validate
:: (Generic (f Maybe)
, Generic (f Identity)
, GValidate (Rep (f Maybe))
(Rep (f Identity)) 3r3589.) 3r33589. => f Maybe
-> Maybe (f Identity)
validate = fmap to. gvalidate from 3r3-3549. 3r? 3572.  3r3-3589. Every time you can get a wide smile when the signature for the function is longer than the actual implementation; that means we hired a compiler to write the code for us. What is important for validation here is that it does not mention 3r-3564. Person ' [/b] ; this function will work for any type defined as high data. Voila! 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. 3r33558. Results 3r33559. 3r? 3572.  3r3-3589. That's all for today guys. We got acquainted with the idea of ​​high-order data, saw how this is completely equivalent to the type of data that was determined in a more traditional way, and also caught a glimpse of what things are possible with this approach. 3r? 3572.  3r3-3589. It allows you to do all sorts of amazing things, such as: generate lenses for arbitrary data types without resorting to Pattern Haskel; 3r33535. sequence [/b] by data type; and automatically track dependencies to use record fields. 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. Happy application of high delivery! 3r? 3572.  3r3-3589. 3r? 3572.  3r3-3589. 3r? 3574. Original: 3r33535. Higher-Kinded Data
3r? 3577. 3r33585. 3r3-3589. 3r3-3589. 3r3-3589. 3r33582. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e .opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d ): d ()}}} t ("//"""_mediator") () (); 3r?383. 3r3-3589. 3r33585. 3r3-3589. 3r3-3589. 3r3-3589. 3r3-3589.
+ 0 -

Add comment