免责声明:当你考虑⊥时,很多东西都不太正确,所以为了简单起见,我将公然忽略这一点。
以下几点:
Note that "union" is probably not the best term for A+B here--that's specifically a disjoint union of the two types, because the two sides are distinguished even if their types are the same. For what it's worth, the more common term is simply "sum type".
Singleton types are, effectively, all unit types. They behave identically under algebraic manipulations and, more importantly, the amount of information present is still preserved.
You probably want a zero type as well. Haskell provides that as Void. There are no values whose type is zero, just as there is one value whose type is one.
这里还少了一个大手术,但我一会儿再讲。
正如你可能已经注意到的,Haskell倾向于借用范畴理论的概念,所有这些都有一个非常直接的解释:
Given objects A and B in Hask, their product A×B is the unique (up to isomorphism) type that allows two projections fst : A×B → A and snd : A×B → B, where given any type C and functions f : C → A, g : C → B you can define the pairing f &&& g : C → A×B such that fst ∘ (f &&& g) = f and likewise for g. Parametricity guarantees the universal properties automatically and my less-than-subtle choice of names should give you the idea. The (&&&) operator is defined in Control.Arrow, by the way.
The dual of the above is the coproduct A+B with injections inl : A → A+B and inr : B → A+B, where given any type C and functions f : A → C, g : B → C, you can define the copairing f ||| g : A+B → C such that the obvious equivalences hold. Again, parametricity guarantees most of the tricky parts automatically. In this case, the standard injections are simply Left and Right and the copairing is the function either.
乘积和和类型的许多性质都可以从上面得到。注意,任何单例类型都是Hask的终端对象,任何空类型都是初始对象。
Returning to the aforementioned missing operation, in a cartesian closed category you have exponential objects that correspond to arrows of the category. Our arrows are functions, our objects are types with kind *, and the type A -> B indeed behaves as BA in the context of algebraic manipulation of types. If it's not obvious why this should hold, consider the type Bool -> A. With only two possible inputs, a function of that type is isomorphic to two values of type A, i.e. (A, A). For Maybe Bool -> A we have three possible inputs, and so on. Also, observe that if we rephrase the copairing definition above to use algebraic notation, we get the identity CA × CB = CA+B.
至于为什么这一切都是有意义的——特别是为什么使用幂级数展开是合理的——请注意,为了演示代数行为,上面的许多内容都引用了类型的“居民”(即具有该类型的不同值)。要明确这种观点:
The product type (A, B) represents a value each from A and B, taken independently. So for any fixed value a :: A, there is one value of type (A, B) for each inhabitant of B. This is of course the cartesian product, and the number of inhabitants of the product type is the product of the number of inhabitants of the factors.
The sum type Either A B represents a value from either A or B, with the left and right branches distinguished. As mentioned earlier, this is a disjoint union, and the number of inhabitants of the sum type is the sum of the number of inhabitants of the summands.
The exponential type B -> A represents a mapping from values of type B to values of type A. For any fixed argument b :: B, any value of A can be assigned to it; a value of type B -> A picks one such mapping for each input, which is equivalent to a product of as many copies of A as B has inhabitants, hence the exponentiation.
虽然一开始很容易将类型视为集合,但在这种情况下实际上并不是很好——我们有不相交的并集,而不是集合的标准并集,没有交集或许多其他集合操作的明显解释,而且我们通常不关心集合成员关系(把它留给类型检查器)。
On the other hand, the constructions above spend a lot of time talking about counting inhabitants, and enumerating the possible values of a type is a useful concept here. That quickly leads us to enumerative combinatorics, and if you consult the linked Wikipedia article you'll find that one of the first things it does is define "pairs" and "unions" in exactly the same sense as product and sum types by way of generating functions, then does the same for "sequences" that are identical to Haskell's lists using exactly the same technique you did.
编辑:哦,这里有一个快速的额外奖励,我认为它可以很好地说明这一点。你在评论中提到,对于树类型T = 1 + T^2,你可以推导出恒等式T^6 = 1,这显然是错误的。然而,T^7 = T是成立的,树和树的七元组之间的双射可以直接构建,参见Andreas Blass的“七棵树合一”。
Edit×2:关于在其他答案中提到的“类型的导数”结构的主题,您可能也会喜欢同一作者的这篇论文,它进一步建立在这个思想之上,包括除法的概念和其他有趣的东西。