I wonder if it's possible to have a function (somewhat similar to dataToTag#
) like:
isFunction# :: a -> Bool
or likely equivalently:
isFunction# :: Any -> Bool
which returns True
iff the value passed in as an argument is of a type a -> b
(or, for that matter, a => b
) at runtime for some types a
and b
, or a newtype
whose underlying type is (so it "sees through" newtype
s, but of course not data
), without forcing its argument. I didn't see anything like this in GHC.Prim myself, but I may have missed something, or maybe it's possible with a manual CMM primop or something.
Now that the question has occurred to me I'm curious about the answer for its own sake (question Y), but the original reason it occurred to me (question X) is that the complaint usually levelled against seq
is that it breaks eta equivalence by making it possible to observe the difference between undefined
and \_ -> undefined
, and I was wondering if it would be possible to make a version of seq
(myseq a = if isFunction# a then flip const a else seq a
) that's still "magically polymorphic" (works forall a
), but simply leaves functions alone.
No, surely not. How could it know without evaluating the argument?
But to address your question X directly, your proposed myseq
is worse than the real seq
since it breaks parametricity. Is myseq undefined :: b -> b
bottom, or the identity? It depends on whether the type variable a
(undefined :: a
) was instantiated with a function type or not.
In Haskell you are always allowed to drop a forall a.
when a
does not appear in the type at all, thanks to parametricity: the choice of a
cannot matter. Your myseq
would lose that property.
This is also why you could not implement isFunction#
without annotating values at runtime with their intended types (isFunction# undefined
is similarly meaningless).
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