Can I get KnownNat n to imply KnownNat (n * 3), etc?

后端 未结 3 1200
灰色年华
灰色年华 2021-01-11 18:14

I\'m working with data types of this shape, using V from linear:

type Foo n = V (n * 3) Double -> Double

Havin

3条回答
  •  滥情空心
    2021-01-11 18:38

    I post another answer as it is more direct, editing the previous won't make sense.

    In fact using the trick (popularised if not invented by Edward Kmett), from reflections reifyNat:

    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE KindSignatures #-}
    {-# LANGUAGE TypeOperators #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE FlexibleContexts #-}
    import GHC.TypeLits
    import Data.Proxy
    import Unsafe.Coerce
    
    newtype MagicNat3 r = MagicNat3 (forall (n :: Nat). KnownNat (n * 3) => Proxy n -> r)
    
    trickValue :: Integer -> Integer
    trickValue = (*3)
    
    -- No type-level garantee that the function will be called with (n * 3)
    -- you have to believe us
    trick :: forall a n. KnownNat n => Proxy n -> (forall m. KnownNat (m * 3) => Proxy m -> a) -> a
    trick p f = unsafeCoerce (MagicNat3 f :: MagicNat3 a) (trickValue (natVal p)) Proxy
    
    test :: forall m. KnownNat (m * 3) => Proxy m -> Integer
    test _ = natVal (Proxy :: Proxy (m * 3))
    

    So when you run it:

    λ *Main > :t trick (Proxy :: Proxy 4) test :: Integer
    trick (Proxy :: Proxy 4) test :: Integer :: Integer
    λ *Main > trick (Proxy :: Proxy 4) test :: Integer
    12
    

    The trick is based on the fact that in GHC the one member class dictionaries (like KnownNat) are represented by the member itself. In KnownNat situation it turns out to be Integer. So we just unsafeCoerce it there. Universal quantification makes it sound from the outside.

提交回复
热议问题