Is (A -> B) /\ (C -> D) a subtype of (A /\ C) -> (B /\ D)?
It seems like it shouldn't be, simply on account of -> being contravariant, but I can't find a good counterexample.
If it is, how can I derive this?
If not, what would a counterexample be?
(To clarify, I'm using /\ for intersection here.)
These types are in a subtype relation, precisely because of contravariance. The union would be a supertype of A and C, so would violate contravariance.
Recall the subtyping rule for functions, which is contravariant in the domain type:
T → U <: T' → U' iff T' <: T and U <: U'
For intersection types, you also have a distributivity rule over arrow types:
(T → U) ∧ (T → V) = T → (U ∧ V)
And of course we have the usual elimination rules for intersection types:
T ∧ U <: T
T ∧ U <: U
Plugging these four rules together, you can easily derive the subtyping you're asking about:
(A → B) ∧ (C → D)
<: (by contravariance and left elimination)
((A ∧ C) → B) ∧ (C → D)
<: (by contravariance and right elimination)
((A ∧ C) → B) ∧ ((A ∧ C) → D)
<: (by distributivity)
(A ∧ C) → (B ∧ D)
FWIW, with union types, you also have the dual distributivity rule:
(U → T) ∨ (V → T) = (U ∨ V) → T
With that, you can analogously derive:
(A → B) ∨ (C → D) <: (A ∨ C) → (B ∨ D)
Related
I can't find bifunctor analog of fmap.
Explanation:
Functor for objects - datatype constructor. Type -
a -> f a
Functor for functions - fmap. Type - (a -> b) -> (fa -> fb)
Bifunctor for objects - result of bimap f g, where f :: (a -> a'), g :: (b -> b'). Type - p a b -> p a' b'
Bifunctor for functions - ?. Type - p (a -> b) (c -> d) -> p (a' -> b') (c' -> d')
Here is why I think bifunctor have such type (am I right?) with some example
UPDATE
UPDATE2
p (a -> b) (c -> d) -> p (a' -> b') (c' -> d') in the image above is morphism from bifunctor to bifunctor and also profunctor (because all functions are profunctors)
Summary:
I've thought p (a -> b) (c -> d) -> p (a' -> b') (c' -> d') is bifunctor for functions, but it's not. Bifunctor for morphisms is bimap. Type: (a -> b) -> (α -> β) -> p a α -> p b β.
I've thought p (a -> b) (c -> d) -> p (a' -> b') (c' -> d') is something unusual, but it's not, it's just function
Functor for objects - datatype constructor. Type - a -> f a
Functor for functions - fmap. Type - (a -> b) -> (fa -> fb)
Although this makes broadly sense, it is important to realise that the arrows above have three different meanings.
Functor for objects - datatype constructor. Type - a ⟼ f a
Functor for functions - fmap. Type - (a ⟿ b) ⟶ (f a ⟿ f b)
where
⟼ is a type-level “maps-to symbol” that associates the type a with a type f a. This does not have anything to do with value-level functions whose domain is a and codomain f a. (Those are found in applicatives/monads, but that's a different story.)
⟿ is a type constructor for some morphism. In the Hask category, those morphisms happen to be Haskell functions, but that's just a special case.
⟶ is an actual function-type constructor.
You may for now forget about the distinction between the latter two, but ⟼ and ⟶ are really quite different conceptually†. Basically, ⟼ is like the arrow you write in a lambda
Maybe :: Type -> Type
Maybe = \a ⟼ Maybe a
whereas ⟶ is just a way to express that you're abstracting over function-things.
Another related thing that might not be clear is that the objects you're talking about are Haskell types. Not values (as OO objects are).
So, I would phrase the listing you gave above thus:
Functor
for objects: datatype constructor. Kind Type -> Type, mapping-association a ⟼ f a.
for morphisms: fmap. Type: (a -> b) -> (f a -> f b).
Bifunctor
for objects: datatype constructor. Kind Type×Type -> Type, or curried Type -> Type -> Type, mapping-association a ⟼ b ⟼ p a b.
for morphisms: bimap. Type: (a -> b) -> (α -> β) -> p a α -> p b β.
†Actually, Haskell does not have ⟼ or what you wrote with a -> f a. This would be a type-level lambda, but type-level functions can actually only be expressed as type families, i.e. the closest you could get to expressing a ⟼ f a is type instance Functored a = f a.
You don't need a Bifunctor instance for (->), just (,):
b1 :: a -> Id a
b2 :: a -> Id2 a
-- instance Bifunctor (,) where
-- bimap f g (x, y) = (f x, g y)
f :: (Int, Float) -> (Id Int, Id2 Float)
f = bimap b1 b2
I hope I got the terminology right.
I want to do the following:
data Hide a = ∀ b. (A.ToJSON (a b), A.ToJSON b) ⇒ Hide (a b)
mapHide ∷ (∀ c. (A.ToJSON (b c), A.ToJSON c) ⇒ a c → b c) → Hide a → Hide b
mapHide f (Hide a) = (Hide $ f a)
Unfortunately, GHC seemingly can't infere the constraints right and complains with:
Could not deduce (A.ToJSON (b b1)) arising from a use of ‘Hide’
from the context (A.ToJSON (a b1), A.ToJSON b1)
Is this somehow possible?
Changing the function type to something like this might do the trick:
(A.ToJSON (b c2), A.ToJSON c2) ⇒
(∀ c. (A.ToJSON (a c), A.ToJSON c) ⇒ a c)
→ b c2)
Also avoid using '$' as it kills polymorphism.
Suppose that, to describe tree-like algebraic data types, we use the following type, Data:
∀(D : *) →
∀(S : *) →
∀(P : *) →
∀(data : S → D) →
∀(add : P → S → S) →
∀(zer : S) →
∀(mul : (D → D) → P → P) →
∀(one : P) →
D
This is, for example, the type List Nat:
λ(D : *) →
λ(S : *) →
λ(P : *) →
λ(data : S → D) →
λ(add : P → S → S) →
λ(zer : S) →
λ(mul : (D → D) → P → P) →
λ(one : P) →
-- data List Nat =
data
-- Cons
(add
-- Nat
(mul (λ(list : D) →
-- data Nat =
data
-- Succ Nat
(add (mul (λ(nat : D) → nat) one)
-- Zero
(add one zer)))
-- List Nat
(mul (λ(list : D) → list) one))
-- Nil
(add one zer))
Now, if we define Data.type as:
λ (type : #Data ) ->
type ? ? ?
(λ (data:?) -> ∀ (a:?) -> (data a))
(λ (ctorType: ?) -> λ (rest:?) -> ∀ (a:?) -> ∀ (ctor:(ctorType a)) -> (rest a))
(∀ (a:?) -> a)
(λ (fieldType: ?) -> λ (rest:?) -> ∀ (a:?) -> ∀ (field:(fieldType a)) -> (rest a))
(∀ (a:?) -> a)
And we apply it to List Nat, ignoring type errors, that normalizes to the type of church-encoded lists of nats, which is ∀(a:*) -> (((∀ (a:*) -> ((a -> a) -> a -> a)) -> a -> a) -> a -> a. I'd like to know:
Is it possible to type the Data.type term?
If not, is there any way to achieve similar effect, using a different strategy?
I have the following definitions for a monoidal category class (Similar to the standard library, but providing inverses of the necessary natural isomorphisms):
class (Category r, Category s, Category t) => Bifunctor p r s t | p r -> s t, p s -> r t, p t -> r s where
bimap :: r a b -> s c d -> t (p a c) (p b d)
--
class (Bifunctor b k k k) => Associative k b where
associate :: k (b (b x y) z) (b x (b y z))
associateInv :: k (b x (b y z)) (b (b x y) z)
--
class (Bifunctor b k k k) => HasIdentity k b i | k b -> i
class (Associative k b, HasIdentity k b i) => Monoidal k b i | k b -> i where
idl :: k (b i a) a
idr :: k (b a i) a
idlInv :: k a (b i a)
idrInv :: k a (b a i)
--
The problem with composing morphisms in a monoidal category using (.) is that the objects may be associated differently. For instance Monoidal Hask (,) (), we might want to compose a morphism of type x -> ((a, b), c) with a morphism of type ((a, ()), (b, c)) -> y. To make the types fit, the natural isomorphism given by bimap idrInv id . associate has to be applied.
Does the Haskell type system enable an automatic way of determining an appropriate isomorphism, based on the desired domain and and codomain type? I can't figure out how to do it.
I figured it out, sort of. The basic idea is to use a multi-parameter type class with a normalizing function and its inverse as methods. The normalizer associates everything to the right, recursively. The type class needs an instance for each case of the recursion. Then, to convert from one way to associate stuff to another, just compose the normalizer for the type of the first morphism and the inverse normalizer for the type of the second morphism.
I will link code here as soon as I will have published it.
I have two closely related questions:
First, how can the Haskell's Arrow class be modeled / represented in Agda?
class Arrow a where
arr :: (b -> c) -> a b c
(>>>) :: a b c -> a c d -> a b d
first :: a b c -> a (b,d) (c,d)
second :: a b c -> a (d,b) (d,c)
(***) :: a b c -> a b' c' -> a (b,b') (c,c')
(&&&) :: a b c -> a b c' -> a b (c,c')
(the following Blog Post states that it should be possible...)
Second, in Haskell, the (->) is a first-class citizen and just another higher-order type and its straightforward to define (->) as one instance of the Arrow class. But how is that in Agda? I could be wrong, but I feel, that Agdas -> is a more integral part of Agda, than Haskell's -> is. So, can Agdas -> be seen as a higher-order type, i.e. a type function yielding Set which can be made an instance of Arrow?
Type classes are usually encoded as records in Agda, so you can encode the Arrow class as something like this:
open import Data.Product -- for tuples
record Arrow (A : Set → Set → Set) : Set₁ where
field
arr : ∀ {B C} → (B → C) → A B C
_>>>_ : ∀ {B C D} → A B C → A C D → A B D
first : ∀ {B C D} → A B C → A (B × D) (C × D)
second : ∀ {B C D} → A B C → A (D × B) (D × C)
_***_ : ∀ {B C B' C'} → A B C → A B' C' → A (B × B') (C × C')
_&&&_ : ∀ {B C C'} → A B C → A B C' → A B (C × C')
While you can't refer to the function type directly (something like _→_ is not valid syntax), you can write your own name for it, which you can then use when writing an instance:
_=>_ : Set → Set → Set
A => B = A → B
fnArrow : Arrow _=>_ -- Alternatively: Arrow (λ A B → (A → B)) or even Arrow _
fnArrow = record
{ arr = λ f → f
; _>>>_ = λ g f x → f (g x)
; first = λ { f (x , y) → (f x , y) }
; second = λ { f (x , y) → (x , f y) }
; _***_ = λ { f g (x , y) → (f x , g y) }
; _&&&_ = λ f g x → (f x , g x)
}
While hammar's answer is a correct port of the Haskell code, the definition of _=>_ is too limited compared to ->, since it doesn't support dependent functions. When adapting code from Haskell, that's a standard necessary change if you want to apply your abstractions to the functions you can write in Agda.
Moreover, by the usual convention of the standard library, this typeclass would be called RawArrow because to implement it you do not need to provide proofs that your instance satisfies the arrow laws; see RawFunctor and RawMonad for other examples (note: definitions of Functor and Monad are nowhere in sight in the standard library, as of version 0.7).
Here's a more powerful variant, which I wrote and tested with Agda 2.3.2 and the 0.7 standard library (should also work on version 0.6). Note that I only changed the type declaration of RawArrow's parameter and of _=>_, the rest is unchanged. When creating fnArrow, though, not all alternative type declarations work as before.
Warning: I only checked that the code typechecks and that => can be used sensibly, I didn't check whether examples using RawArrow typecheck.
module RawArrow where
open import Data.Product --actually needed by RawArrow
open import Data.Fin --only for examples
open import Data.Nat --ditto
record RawArrow (A : (S : Set) → (T : {s : S} → Set) → Set) : Set₁ where
field
arr : ∀ {B C} → (B → C) → A B C
_>>>_ : ∀ {B C D} → A B C → A C D → A B D
first : ∀ {B C D} → A B C → A (B × D) (C × D)
second : ∀ {B C D} → A B C → A (D × B) (D × C)
_***_ : ∀ {B C B' C'} → A B C → A B' C' → A (B × B') (C × C')
_&&&_ : ∀ {B C C'} → A B C → A B C' → A B (C × C')
_=>_ : (S : Set) → (T : {s : S} → Set) → Set
A => B = (a : A) -> B {a}
test1 : Set
test1 = ℕ => ℕ
-- With → we can also write:
test2 : Set
test2 = (n : ℕ) → Fin n
-- But also with =>, though it's more cumbersome:
test3 : Set
test3 = ℕ => (λ {n : ℕ} → Fin n)
--Note that since _=>_ uses Set instead of being level-polymorphic, it's still
--somewhat limited. But I won't go the full way.
--fnRawArrow : RawArrow _=>_
-- Alternatively:
fnRawArrow : RawArrow (λ A B → (a : A) → B {a})
fnRawArrow = record
{ arr = λ f → f
; _>>>_ = λ g f x → f (g x)
; first = λ { f (x , y) → (f x , y) }
; second = λ { f (x , y) → (x , f y) }
; _***_ = λ { f g (x , y) → (f x , g y) }
; _&&&_ = λ f g x → (f x , g x)
}