Hi Alexander,

Nice idea to test against the set of known values. That's more type-safe than 
anything I've thought of. I agree that it's a bit of a painful construction, 
but I don't think we can do better with type-lits, as there is limited 
reasoning ability that GHC can manage. If you want to switch to unary naturals 
(`data Nat = Zero | Succ Nat`), then this can be built somewhat nicely with 
singletons and without unsafeCoerce. But, of course, unary naturals are very 
slow.

By the way, the bug in the Proof2 version is a bug in GHC 7.8.3 (only in .3 -- 
not in .2 or in the soon-to-be .4) that allows you to write unsaturated type 
families that don't work. Saying `LessThan255` without a parameter should be a 
syntax error, but that check was accidentally turned off for 7.8.3, leading to 
a bogus type error.

Thanks for sharing this work!
Richard

On Nov 28, 2014, at 5:27 PM, Alexander V Vershilov 
<alexander.vershi...@gmail.com> wrote:

> 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

Reply via email to