Given a functor (or any type constructor) f
, we can get a "version" of that functor that does not contain a value of its argument. We just define newtype NoArg f = NoArg (f Void)
. For example:
NoArg []
is just the empty list.NoArg Maybe
is just Nothing.NoArg (Either e)
is just e
.NoArg (Identity)
is Void
.NoArg IO
is an IO action that produces effects forever (like a server).Functor f => NoArg (Free f)
is Fix f
.My question is if we can do the opposite, and create a type of the constructors of a Functor that does use its argument. Formally, Arg :: (* -> *) -> (* -> *)
should be such that there is a term forall a. Arg f a -> a
or equivalently Arg f Void -> Void
. For example:
Arg [] a
is the type of non empty lists of type a
.Arg Maybe a
is just a
.Arg (Either e) a
is just a
.Arg Identity a
is just a
.Arg IO a
you would think is IO actions that produce a result. This probably will not be the case though since you there is no function from IO a
to a
, or even Maybe a
that isn't const Nothing
.Functor f => Arg (Free f) a
is Free (Arg f) a
.I'm thinking Arg f
would be some sort of "supremum" of the functors g
that embed in f
such that there exists a term Argful g :: g Void -> Void
.
EDIT: I guess the true test would be for Arg [] a
to be isomorphic to NomEmpty a
, where
data NonEmpty a = One a | Cons a (NonEmpty a)
I doubt there's a solution in Haskell, but there's a fairly simple definition in languages with dependent pairs and equality types. I work in Idris below.
First, we say that two elements in an f
functor have the same shape if they become equal after being filled with ()
:
SameShape : Functor f => f a -> f b -> Type
SameShape fa fb = (map (const ()) fa = map (const ()) fb)
The elements of Arg f a
are elements of f a
such that there are no elements of f Void
with the same shape.
Arg : (f : Type -> Type) -> Functor f => Type -> Type
Arg f a = (fa : f a ** ((fv : f Void) -> SameShape fa fv -> Void))
**
denotes a dependent pair where the component on the right may refer to the first component. This definition excludes exactly those values which don't contain a
. So, we have the desired property:
lem : Functor f => Arg f Void -> Void
lem (fv ** p) = p fv Refl
where Refl
proves map (const ()) fv = map (const ()) fv
.
This doesn't work for IO
, but I don't expect there's any sensible definition for that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With