
Once in a while, one desires to tie a recursive knot and is stymied by a strict data constructor. I recently encountered this problem trying to improve the implementation of `never` in the `streaming` package. The Stream type is defined thus: data Stream f m r = Step !(f (Stream f m r)) | Effect (m (Stream f m r)) | Return r It would be nice to be able to write never :: Applicative f => Stream f m r never = fix (Step . pure) Unfortunately, if `pure` is strict (e.g., Identity), this won't work. The Step wrapper attempts to force `pure never` and then apply the Step worker. This will never work. The streaming package works around the problem by representing the `never` stream in a different way, at the potential cost of some efficiency. In other contexts, there may be no (safe) workaround at all. This is terribly frustrating, because it seems almost possible to express what I want in Core, and even possible to express it in Haskell with a really awful unsafeCoerce. The nasty version looks like this: data StreamL f m r = StepL (f (StreamL f m r)) | EffectL (m (StreamL f m r)) | ReturnL r never :: forall f m r. Applicative f => Stream f m r never = case loop of StepL x -> x `pseq` unsafeCoerce loop where loop :: StreamL f m r loop = StepL (pure loop) in loop {-# NOINLINE loop #-} {-# NOINLINE never #-} That is, I make a copy of the Stream type, but with a lazy version of the Step constructor, I tie my knot, I make very sure that the strict field is evaluated, and then I unsafeCoerce the whole thing in a thoroughly unsupported fashion to get back to the right type. Ideally, It would be nice to get GHC to manufacture, and expose to users, bidirectional patterns that offer more access to the raw representation of a type. Basically, I want to get a bidirectional pattern for Step that: 1. Is lazy when used as a constructor (applying the "worker" constructor directly) 2. Is viewed as lazy on matching, so the strictness analysis comes out right. Using such a feature will presumably always be dangerous (unless someone does a ton of work to find an efficient way to make it safe), but I'd rather have a reasonable dangerous way to do it than an unreasonable dangerous way, if that can be accomplished. Unfortunately, I haven't been able to think of a reasonable design for such a language feature. Does anyone else have any ideas? Or some other thought about how such things might be done? David