Hi, Bas, Richard. I've played a bit with example, obvously first approach contained bugs, but seems that I have fixed it and here I have 2 approaches, one uses unsafeCoerce (as Richard suggested) another is safe but with bad complexity:
Full file is quite big, so I'm not inlining it in mail, but here is a link: https://github.com/qnikst/haskell-fun/blob/master/typelevel-literals/Proof.lhs I wonder how far it's possible to go with singletons approach that I have not tried yet. -- Alexander On 26 November 2014 at 10:15, Bas van Dijk <v.dijk....@gmail.com> wrote: > Hi Alexander, > > Thanks for your answer! This provides a lot of ideas how to proceed. > > I'm unsure about the following though: > >> lessThen :: KnownNat m => SomeNat -> Proxy m -> Maybe (Proof (n <= m) (Proxy >> n)) >> lessThen (SomeNat p) k >> | natVal p <= natVal k = Just (Proof $ Tagged (Proxy :: Proxy n)) >> | otherwise = Nothing > > Doesn't this mean lessThen returns a Proxy n for all (n :: Nat) and > not just the Nat inside the SomeNat? > > I also see that p is only used for comparing it to k. It's not used to > produce the return value. > > Cheers, > > Bas > > On 25 November 2014 at 19:55, Alexander V Vershilov > <alexander.vershi...@gmail.com> wrote: >> Hi, Richard, Bas. >> >> Maybe I didn't spell it properly but my point was to create a data >> type that carry a proof >> without exposing it's constructor and having clever constructor only, >> then the only place >> where you need to check will be that constructor. >> >> Also it's possible to write in slightly clearer, but again it's >> possible to make a mistake here >> and it will be a false proof. >> >>> lessThen :: KnownNat m => SomeNat -> Proxy m -> Maybe (Proof (n <= m) >>> (Proxy n)) >>> lessThen (SomeNat p) k >>> | natVal p <= natVal k = Just (Proof $ Tagged (Proxy :: Proxy n)) >>> | otherwise = Nothing >> >> Of cause solution using singletons could solve this problem much better. >> >> -- >> Alexander >> >> On 25 November 2014 at 21:34, Richard Eisenberg <e...@cis.upenn.edu> wrote: >>> Hi Bas, >>> >>> I believe to do this "right", you would need singleton types. Then, when >>> you discover that the number is bounded by 255, you would also discover >>> that the type is bounded by 255, and you'd be home free. >>> >>> Unfortunately, I there isn't currently a way to do comparison on >>> GHC.TypeLits Nats with singletons. (There is a module >>> Data.Singletons.TypeLits in the `singletons` package, but there's a comment >>> telling me TODO in the part where comparison should be implemented.) If it >>> were implemented, it would have to use unsafeCoerce, as there's no built-in >>> mechanism connecting runtime numbers with TypeLits. >>> >>> If I were you, I would just write `g` using unsafeCoerce in the right spot, >>> instead of bothering with all the singletons, which would have to use >>> unsafety anyway. >>> >>> The solution Alexander provides below doesn't quite build a proof, I think. >>> Tellingly, if we omit the `natVal p <= 255` check, everything else still >>> compiles. Thus, the `Proof` type he uses can be built even if the fact >>> proven is false. That said, I don't know if my solution is any better, >>> crucially relying on unsafeCoerce. >>> >>> Richard >>> >>> On Nov 25, 2014, at 4:52 AM, Alexander V Vershilov >>> <alexander.vershi...@gmail.com> wrote: >>> >>>> Hi, >>>> >>>> Following approach can work, the idea is to define a type that will >>>> carry a proof (constraint) that we want to check. Here I have reused >>>> Data.Tagged, but it's possible to introduce your own with concrete >>>> constraints. >>>> >>>>> {-# LANGUAGE DataKinds #-} >>>>> {-# LANGUAGE GADTs #-} >>>>> {-# LANGUAGE TypeOperators #-} >>>>> {-# LANGUAGE KindSignatures #-} >>>>> {-# LANGUAGE PolyKinds #-} >>>>> {-# LANGUAGE UndecidableInstances #-} >>>>> import GHC.TypeLits >>>>> import GHC.Exts >>>>> import Data.Proxy >>>>> import Data.Tagged >>>>> import System.Environment >>>> >>>> New constraint carrying data type >>>> >>>>> newtype Proof a b = Proof { unProof :: Tagged a b } >>>> >>>> Runtime check for unknown naturals >>>> >>>>> fromSome :: SomeNat -> Maybe (Proof (n <= 255) (Proxy n)) >>>>> fromSome (SomeNat p) >>>>> | natVal p <= 255 = Just (Proof $ Tagged (Proxy :: Proxy n)) >>>>> | otherwise = Nothing >>>> >>>> Compiletime converter for known naturals >>>> >>>>> fromKnown :: (KnownNat n, n <= 255) => Proxy n -> Proof (n <= 255) >>>>> (Proxy n) >>>>> fromKnown n = Proof $ Tagged n >>>> >>>> Function to test: >>>> >>>>> f2 :: (c ~ (n <= 255)) => Proof c (Proxy n) -> () >>>>> f2 _ = () >>>> >>>> Example of use: >>>> >>>>> main :: IO () >>>>> main = do >>>>> [arg] <- getArgs >>>>> let n = read arg :: Integer >>>>> >>>>> case someNatVal n of >>>>> Nothing -> error "Input is not a natural number!" >>>>> Just sn -> case fromSome sn of >>>>> Just p -> return $ f2 p >>>>> _ -> error "Input if larger than 255" >>>> >>>> >>>> On 25 November 2014 at 10:51, Bas van Dijk <v.dijk....@gmail.com> wrote: >>>>> Hi, >>>>> >>>>> I have another type-level programming related question: >>>>> >>>>>> {-# LANGUAGE GADTs #-} >>>>>> {-# LANGUAGE TypeOperators #-} >>>>>> {-# LANGUAGE ScopedTypeVariables #-} >>>>>> {-# LANGUAGE KindSignatures #-} >>>>>> >>>>>> import GHC.TypeLits >>>>> >>>>> Say I have a Proxy p of some type-level natural number: >>>>> >>>>>> p :: forall (n :: Nat). Proxy n >>>>>> p = Proxy >>>>> >>>>> Imagine I get p from user input like this: >>>>> >>>>>> main :: IO () >>>>>> main = do >>>>>> [arg] <- getArgs >>>>>> let n = read arg :: Integer >>>>>> >>>>>> case someNatVal n of >>>>>> Nothing -> error "Input is not a natural number!" >>>>>> Just (SomeNat (p :: Proxy n)) -> ... >>>>> >>>>> I also have a function f which takes a proxy of a natural number but >>>>> it has the additional constraint that the number should be lesser than >>>>> or equal to 255: >>>>> >>>>>> f :: forall (n :: Nat). (n <= 255) => proxy n -> () >>>>>> f _ = () >>>>> >>>>> How do I apply f to p? >>>>> >>>>> Obviously, applying it directly gives the type error: >>>>> >>>>>> f p >>>>> <interactive>:179:1: >>>>> Couldn't match expected type ‘'True’ with actual type ‘n0 <=? 255’ >>>>> The type variable ‘n0’ is ambiguous >>>>> In the expression: f p >>>>> In an equation for ‘it’: it = f p >>>>> >>>>> I imagine I somehow need to construct some Proof object using a function >>>>> g like: >>>>> >>>>>> g :: forall (n :: Nat). proxy n -> Proof >>>>>> g _ = ... >>>>> >>>>> Where the Proof constructor encapsulates the (n <= 255) constraint: >>>>> >>>>>> data Proof where >>>>>> NoProof :: Proof >>>>>> Proof :: forall (n :: Nat). (n <= 255) >>>>>> => Proxy n -> Proof >>>>> >>>>> With g in hand I can construct c which patterns matches on g p and >>>>> when there's a Proof the (n <= 255) constraint will be in scope which >>>>> allows applying f to p: >>>>> >>>>>> c :: () >>>>>> c = case g p of >>>>>> NoProof -> error "Input is bigger than 255!" >>>>>> Proof p -> f p >>>>> >>>>> But how do I define g? >>>>> >>>>> Cheers, >>>>> >>>>> Bas >>>>> _______________________________________________ >>>>> Glasgow-haskell-users mailing list >>>>> Glasgow-haskell-users@haskell.org >>>>> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users >>>> >>>> >>>> >>>> -- >>>> Alexander >>>> _______________________________________________ >>>> Glasgow-haskell-users mailing list >>>> Glasgow-haskell-users@haskell.org >>>> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users >>>> >>> >> >> >> >> -- >> Alexander -- Alexander _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users