Skip to content
This repository was archived by the owner on Aug 27, 2022. It is now read-only.

Haskell Generics tutorial - #40

Merged
mrkkrp merged 4 commits into
tutorialsfrom
generics-tutorial
Feb 1, 2017
Merged

Haskell Generics tutorial#40
mrkkrp merged 4 commits into
tutorialsfrom
generics-tutorial

Conversation

@mrkkrp

@mrkkrp mrkkrp commented Jan 22, 2017

Copy link
Copy Markdown
Contributor

Ready for review.

@mrkkrp
mrkkrp force-pushed the generics-tutorial branch 3 times, most recently from e2e09e2 to 044bc11 Compare January 27, 2017 12:53
@mrkkrp
mrkkrp force-pushed the generics-tutorial branch from 044bc11 to 4814873 Compare January 27, 2017 14:21
@@ -0,0 +1,3 @@
resolver: lts-7.16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though not that important, lts-7.17 is out

@Nenlantea Nenlantea left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Mark, I've added some comments to the tutorial, please take a look.

Comment thread tutorials/haskell/generics/tutorial.md Outdated
description: The tutorial will get you up to speed with GHC generics quickly.
---

The tutorial will get you up to speed with GHC generics quickly. It should

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "This tutorial" is clearer than "The tutorial"

Comment thread tutorials/haskell/generics/tutorial.md Outdated
Let's start from the beginning. What is generics? **Generics**, like in
other languages, **is a way to use the same code to manipulate different
data types**. We should note that in this regard the technique is very close
to much more common notion of polymorphism, which in Haskell comes in two

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider changing "to much more common" for "to the much more common"

Comment thread tutorials/haskell/generics/tutorial.md Outdated
flavors:

* **Parametric polymorphism**, when we have type variables in functions/data
types. This allows the same function, such as `const` work with any types

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing "such as const work" to "such as const to work"

Comment thread tutorials/haskell/generics/tutorial.md Outdated

* **Parametric polymorphism**, when we have type variables in functions/data
types. This allows the same function, such as `const` work with any types
of arguments, as long as the more general types from function's signature

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing "from function's signature" to "from a function's signature"

Comment thread tutorials/haskell/generics/tutorial.md Outdated
these properties. The code is then applicable to any data type that has
the properties.

How generics are different? Generics allow to describe transformation that

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be "How are generics different?" instead of "how generics are different?" because it's a question.

Comment thread tutorials/haskell/generics/tutorial.md Outdated

Representation also has associated metadata. It may look messy and difficult
to read, but I'll explain you the logic behind it in a moment, it should
clarify why it's done like this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add "help to" before "clarify"

Comment thread tutorials/haskell/generics/tutorial.md Outdated
once.

The `c` type is auto-generated by the compiler and encodes metadata on type
level. Why not to store the metadata on value level, in `M1` constructor?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove "to" from "Why not to store the metadata on value level, in M1 constructor?" leaving "Why not store the metadata on value level, in M1 constructor?"


## Example: counting constructor fields

I think now we're dangerous enough to implement a simple (and pretty

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing "dangerous" with "adventurous"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, it should be dangerous :-D

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like "dangerous" too. :P

Comment thread tutorials/haskell/generics/tutorial.md Outdated
```

This way we can eat the cake and have it: deriving is easy and otherwise no
ugly details is visible!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change "is" for "are" because you are referring to the plural "details".

Comment thread tutorials/haskell/generics/tutorial.md Outdated
* [The `generics-sop` package on Hackage](https://hackage.haskell.org/package/generics-sop)
* [Typeable and Data in Haskell](http://chrisdone.com/posts/data-typeable) —
the blog by Chris Done contains some straightforward examples of generic
programming using `Typeable` and `Data` type class

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add at the end a you might also like to read list of blogs or tutorials you thing the audience of this tutorial might like from our own site, we'd like to give people an outlook on other interesting things we've been working on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this. The section should be relevant to with respect to the topic of the tutorial. If we want to add a section of links like "other our articles you might be interested in", it should be done on higher level, maybe in the template that is used to render tutorials, not in text of tutorial. This is just my opinion though.

@Nenlantea

Copy link
Copy Markdown

@juanpaucar can you please do a technical review on this?

@mrkkrp

mrkkrp commented Jan 27, 2017

Copy link
Copy Markdown
Contributor Author

@Nenlantea Thank you for your review, I've made the necessary corrections.

---

This tutorial will get you up to speed with GHC generics quickly. It should
be noted that generics is not something academic and useless, quite the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Punctuation/grammar tweaks:
...and useless; quite the...
...(and errors associated with it)...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
overhead. In fact, by the time you get to the end of the tutorial, I hope
you'll agree that generics are easy to use and easy “to get” as well.

Let's start from the beginning. What is generics? **Generics**, like in

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... what are generics?...
...languages, are a way...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll agree on this, although I talked about generics as a language feature, but perhaps if the language feature's name is in plural form, this version is more correct.

Comment thread tutorials/haskell/generics/tutorial.md Outdated
signature can be made equal to concrete types we want to work with by
instantiating/fixing the type variables.

* **Ad-hoc polymorphism**, which allows to perform a computation abstracted

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... allows us to perform...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
## The idea about generics

Haskell's mechanism that opens the possibility for such a thing as generics
is, of course, type classes and ad-hoc polymorphism. The ability to describe

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the "of course"; we should assume that nothing is obvious to the reader.

Comment thread tutorials/haskell/generics/tutorial.md Outdated
Haskell's mechanism that opens the possibility for such a thing as generics
is, of course, type classes and ad-hoc polymorphism. The ability to describe
a data type in terms of a set of combinators is our property, encapsulated
by the `Generic` type class. Data type that is an instance of that type

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... A data type that is and instance of the Generic type class... (I would be redundant here for clarity)
... work with a representation of...

Comment thread tutorials/haskell/generics/tutorial.md Outdated

The process can be described like this:

1. Given data type gets `Generic` instance automatically, as it can be

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... A given data type gets a Generic ...
... help of the DeriveGeneric...

magically get all the useful methods of that type class.

If you know about `Data` and `Typeable` type classes, then you probably know
that it's possible to do something similar using information which methods

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... using the information those type classes provide. Data and Typable are beyond the scope of this tutorial, but you can start with ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data and Typeable are not libraries, they are standard type classes :-)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't say anything about Data and Typable being libraries, so I'm not sure what your comment is referring to.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I'm pretty sure it did say somethnig of the sort before you edited it :-) It doesn't matter though.

tutorial, we won't touch this topic. You can start with
this [blog post by Chris Done](http://chrisdone.com/posts/data-typeable) if
you're interested.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... is not as simple as ...


Step 2 is not so simple as it sounds though, at least not until we examine
the details of this business more closely.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... represent the shape of a data type...

What could they look like? Well, if I showed you the data types as they are
right now, you would probably run away in fear, cursing the tutorial and
Haskell's generics. I have a better idea. Let's start with the simplest
thing possible and then iterate asking ourselves how to work around some

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... that we might need to support ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
we arrive at the definitions that are actually used.

For better or worse, algebraic data types lock us into a view on the world
that consists of sums and products (another Haskell's feature that makes

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...another Haskell feature that makes the existence of a generics framework possible; generics ...
Could we make GADTs a link to another tutorial or some documentation about GADTs?


* Constructors without arguments. Let this be something like this: `data U1
= U1`.

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are L1 and R1 and why are they unnecessary for the product types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'll be obvious to anyone who is familiar with Haskell's data types. In short, sum data type means that you can have one of several options:

data Foo = Foo | Bar | Baz

This type is inhibited by 3 values, represented by three data constructors.

To represent it generically we would need something like this:

Foo = L1 U1
Bar = R1 (L1 U1)
Baz = R1 (R1 U1)

You see, we need need this sum type to hold two data constructor “options” and then we can nest them and represent any sum type.

With product types, you multiple number of possible values that inhibit a type, i.e. with a tuple:

(Foo, Foo)

Number of possible values is 3 × 3 = 9, because tuple includes one Foo value and another one. This is why this is called product types. This is why this is called algebraic data types. So with products you don't need two different constructors, because you have “everything at once”.

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am quite familiar with algebraic data types in Haskell, but what you explained in the above comment was not obvious to me. Perhaps this should have been obvious to me, but I am the target audience for this blog post, so you should assume that this will not be obvious to the reader. The above explanation, perhaps a little more concisely, would be a great addition to the tutorial.

Comment thread tutorials/haskell/generics/tutorial.md Outdated
* Products: `data (f :*: g) = f :*: g`.

Not bad for a start. Let's try use this representation to derive a `Functor`
instance. Deriving a such an instance means that we should provide `fmap`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... provide the function fmap, which looks ...

```haskell
fmap :: (a -> b) -> Rep f -> Rep f
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(replace "where" with) ...Here, Rep f maps to the type of the generic ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
fmap :: (a -> b) -> Rep f -> Rep f
```

…where `Rep f` maps to the type of generic representation of `f :: * -> *`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... the functor's inner ...

extra `p` parameter as a dummy type index that has no meaning.

The authors of the generic extension went with the second option, and I
can't blame them. We'll see that there are a lot of wrappers already and we

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little confused by this sentence: We'll see that there ...
What are the "wrappers" in this case? Would we have more or fewer with option 1 above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it should become clearer as you read though the tutorial (will be even clearer on second read). It also requires some understanding of Haskell to get it though.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a bit clearer on a second look. The first option would define a whole other set of "wrappers" on top of the ones we have already defined.


With `Rec0`, we can finally build representation of `Maybe a`:

```haskell

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... is the type of ...


Let's derive a different representation that works with `* -> *` kinds:

```haskell

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... The type of ...

-- |
-- +--- L1 and R1 are from our representation of sum types
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add in a sentence or two explaining the motivation for coming up with a second representation of Maybe? Also, which representation is the one actually used by ghc? Or are both used depending on context?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both are useful (and used). GHC can derive the first representation when you derive Generic and the second one when you derive Generic1. This is explained later in the tutorial.

Yes, we could add something. I guess I did a bad job if it's not clear, this is one of main points of the tutorial. Or maybe the topic is just hairy for less-experienced Haskellers.


Type classes that map types to their representations are called `Generic`
(for type classes that work with `*` kinds) and `Generic1` (for type classes
that work with `* -> *` kinds). They live in the module called

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...GHC.Generics, together ...
... build the data type ...

```

`from` and `from1` map values of data types to their generic
representations. `Rep` and `Rep1` are associated type functions (the feature

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... that take the type of data ...
... and yield the type of its ...

actual value of target data type. This is done via `to` and `to1`. The good
thing about this, of course, that GHC can derive `Generic` and `Generic1`
for us automatically with `DeriveGeneric` language extension enabled.

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...Now let's open GHCi and try to infer Rep for some type: ...
Perhaps we should be even more explicit about how to start GHCi to get this output. We could tell them to clone the repo, run stack init, then stack ghci or stack repl.

Ah OK, there is just a little bit more to this representation thing…

## Metadata wrappers

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... A representation also ...

```haskell
newtype M1 i c f p = M1 { unM1 :: f p } -- ‘f p’ is what lives inside, U1 for example
-- ^ ^
-- | |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... allows us to get ...

The `c` type is auto-generated by the compiler and encodes metadata on type
level. Why not store the metadata on value level, in `M1` constructor? Well,
if it were there, we would have to provide it if we wanted to generate some
values, and providing metadata for already existing data type is certainly

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... only the compiler can ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
level. Why not store the metadata on value level, in `M1` constructor? Well,
if it were there, we would have to provide it if we wanted to generate some
values, and providing metadata for already existing data type is certainly
something that only compiler can do properly. With the current approach,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... approach, a given data type determines the type of its representation, including its metatdata, ...

* Every constructor is wrapped in `C1`, which provides information about
constructor (such as constructor name, fixity, and whether it's a record).

* Every argument of constructor is wrapped with `S1` (even if it's not

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... tells us the selector ...


Look
[at the Haddock](https://hackage.haskell.org/package/base-4.8.2.0/docs/GHC-Generics.html#t:Datatype),
to find out names of functions that help extract the metadata. Using them is

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is the "wrapped data" a type representation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrappers are part of the representation. As we have seen wrapping in M1 or L1 adds context to whatever is inside of it.

instance (CountFields (a p), CountFields (b p)) => CountFields ((a :*: b) p) where
countFields (a :*: b) = countFields a + countFields b
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Let's write ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
defaultCountFields = countFields . from
```

But here is the catch, the code above does not compile. The `CountFields`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question 1: What is this kind Constraint?
Question 2: How can a type class have a kind? I thought only types had kinds.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constraints have the kind Constraint :-) For example show :: Show a => a -> String, where the Show a thing is not something magical, you can manipulate it too, for example you can get it from a type function. So it needs a kind.

Type class does not have a kind per se, but every type class has a corresponding constraint (like Show type class has Show :: * -> Constraint, because we need a way to express the idea of constraint and it should be well-kinded, so here for example it says give me a type and I'll give you a constraint that will require that type to have an instance of Show type class).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great, and makes things much clearer for me, thanks! I think this explanation would also enrich the blog post if added in.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also in this case, we should remove "The" and begin the sentence with CountFields.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also be more explicit, and rather than saying "something", we could say:
... but we are giving it (Rep a), which has the kind ...

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even:
... giving it (Rep a) :: * -> *

But here is the catch, the code above does not compile. The `CountFields`
has the kind `CountFields :: * -> Constraint`, but we give it something of
kind `* -> *`. It's not going to take our abuse like this!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the CountFields1 "helper class" in addition to our original CountFields1 class, or does it replace it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need both. It's typical (when you work with generics) for type classes that work with * things to need an auxiliary type class like this in addition to the main type class.

Having dealt with generic implementation of the functionality of interest,
let's put it all together and use a special GHC extension to allow the user
derive type classes without knowing anything about generics.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... For a generic implementation ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
let's put it all together and use a special GHC extension to allow the user
derive type classes without knowing anything about generics.

For generic implementation to work without user's definition we need to

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... seen, a generic ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
derive type classes without knowing anything about generics.

For generic implementation to work without user's definition we need to
provide it as the default definition. As you have already seen, generic

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... involves a Generic constraint, and ...

language extension, allows to give a different type signature for default
implementation of a type class:

```haskell

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be class Functor f where?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good catch.

provide it as the default definition. As you have already seen, generic
implementation often involves `Generic` constraints and it would be ugly to
add them to every type class (on the left hand side of `=>`) just to make
deriving easier. The `default` keyword, enabled by the `DefaultSignartures`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... allows us to give ...

default countFields :: (Generic a, CountFields1 (Rep a)) => a -> Natural
countFields = defaultCountFields
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... can have our cake and eat it too: ...

ugly details are visible!

## Conclusion

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider rephrasing as:
Generics are a powerful means of automating the process of defining type class instances, which is error-prone and boring if done by hand.

Comment thread tutorials/haskell/generics/tutorial.md Outdated

## Conclusion

Generics is a powerful means to automate error-prone and boring deriving.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... However, this feature is helpful beyond ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
## Conclusion

Generics is a powerful means to automate error-prone and boring deriving.
The feature is however helpful beyond just that use-case, as with a bit of

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... allows us to reason ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated

Generics is a powerful means to automate error-prone and boring deriving.
The feature is however helpful beyond just that use-case, as with a bit of
creativity it allows to reason about data types generically (for example, we

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... could calculate the number of ...

Comment thread tutorials/haskell/generics/tutorial.md Outdated
in a type safe way (useful, for example, in combination with Quick Check's
value generation). Finally, there are quite a few very interesting packages
that complement or build on top of GHC generics. Once you feel comfortable
with vanilla generics,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... libraries like generic ...

* [Documentation for `GHC.Generics` on Hackage](https://hackage.haskell.org/package/base-4.8.2.0/docs/GHC-Generics.html)
* [The page on Haskell Wiki](https://wiki.haskell.org/GHC.Generics)
* [The `generics-sop` package on Hackage](https://hackage.haskell.org/package/generics-sop)
* [Typeable and Data in Haskell](http://chrisdone.com/posts/data-typeable) —

@nlander nlander Jan 31, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... This blog post by ...
... using the Typable and Data type classes.

@@ -0,0 +1,21 @@
MIT License

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark,
Thanks for writing this post! It is really well written and structured. I learned a lot editing it. I have tried to understand Generics before and found myself with a headache trying to take in too much at once. Saving the MetaData info for the end was a wise choice.
Most of my comments are small grammar fixes about missing articles, subjects an objects, e.g. a, the, we and us. My biggest question is about this tricky p parameter. It seems like that is a hard thing to explain, but I hope my questions in the comments below give you some inspiration for how to explain it just a little more clearly.
Cheers!
Nathan

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@mrkkrp
mrkkrp force-pushed the generics-tutorial branch from 975e730 to 3dacde5 Compare February 1, 2017 12:36
@mrkkrp
mrkkrp force-pushed the generics-tutorial branch from 3dacde5 to 52cfa21 Compare February 1, 2017 12:44
@mrkkrp
mrkkrp merged commit 46bc4fc into tutorials Feb 1, 2017
@mrkkrp
mrkkrp deleted the generics-tutorial branch February 1, 2017 14:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants