llvm-extra 0.10 → 0.10.1
raw patch · 8 files changed
+307/−118 lines, 8 filesdep ~enumset
Dependency ranges changed: enumset
Files
- llvm-extra.cabal +4/−2
- src/LLVM/Extra/Control.hs +5/−0
- src/LLVM/Extra/Function.hs +112/−0
- src/LLVM/Extra/Memory.hs +31/−107
- src/LLVM/Extra/Multi/Value/Private.hs +56/−8
- src/LLVM/Extra/Multi/Vector.hs +19/−0
- src/LLVM/Extra/Storable/Private.hs +1/−1
- src/LLVM/Extra/Struct.hs +79/−0
llvm-extra.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: llvm-extra-Version: 0.10+Version: 0.10.1 License: BSD-3-Clause License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -61,7 +61,7 @@ default: False Source-Repository this- Tag: 0.10+ Tag: 0.10.1 Type: darcs Location: http://code.haskell.org/~thielema/llvm-extra/ @@ -101,7 +101,9 @@ LLVM.Extra.MaybeContinuation LLVM.Extra.Either LLVM.Extra.Tuple+ LLVM.Extra.Struct LLVM.Extra.Control+ LLVM.Extra.Function LLVM.Extra.Array LLVM.Extra.Scalar LLVM.Extra.Vector
src/LLVM/Extra/Control.hs view
@@ -363,6 +363,11 @@ -- * return with better type inference +{- |+'ret' terminates a basic block which interferes badly+with other control structures in this module.+If you use the control structures then better use "LLVM.Extra.Function".+-} ret :: Value a -> CodeGenFunction a () ret = LLVM.ret
+ src/LLVM/Extra/Function.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE TypeFamilies #-}+{- |+Alternative to 'LLVM.Core.defineFunction'+that creates the final 'LLVM.Core.ret' instruction for you.+-}+module LLVM.Extra.Function (+ C,+ CodeGen,+ define,+ create,+ createNamed,+ Return, Result, ret,+ ) where++import qualified LLVM.Util.Proxy as LP+import qualified LLVM.Core as LLVM++import Foreign.StablePtr (StablePtr)+import Foreign.Ptr (Ptr, FunPtr)++import Control.Applicative ((<*>))++import Data.Int (Int8, Int16, Int32, Int64)+import Data.Word (Word8, Word16, Word32, Word64, Word)+++define ::+ (C f) => LLVM.Function f -> CodeGen f -> LLVM.CodeGenModule ()+define fn body =+ LLVM.defineFunction fn (addRet (proxyFromElement2 fn) body)++proxyFromElement2 :: f (g a) -> LP.Proxy a+proxyFromElement2 _ = LP.Proxy+++create ::+ (C f) =>+ LLVM.Linkage -> CodeGen f -> LLVM.CodeGenModule (LLVM.Function f)+create linkage body = do+ f <- LLVM.newFunction linkage+ define f body+ return f++createNamed ::+ (C f) =>+ LLVM.Linkage -> String -> CodeGen f -> LLVM.CodeGenModule (LLVM.Function f)+createNamed linkage name body = do+ f <- LLVM.newNamedFunction linkage name+ define f body+ return f+++{- |+> CodeGen (a->b->...-> IO z) =+> Value a -> Value b -> ... CodeGenFunction r (Value z)@.+-}+class LLVM.FunctionArgs f => C f where+ type CodeGen f :: *+ addRet :: LP.Proxy f -> CodeGen f -> LLVM.FunctionCodeGen f++instance (C b, LLVM.IsFirstClass a) => C (a -> b) where+ type CodeGen (a -> b) = LLVM.Value a -> CodeGen b+ addRet proxy f a = addRet (proxy<*>LP.Proxy) (f a)++instance Return a => C (IO a) where+ type CodeGen (IO a) = LLVM.CodeGenFunction a (Result a)+ addRet LP.Proxy code = code >>= ret+++class (LLVM.IsFirstClass a) => Return a where+ type Result a+ ret :: Result a -> LLVM.CodeGenFunction a ()+instance Return () where+ type Result () = ()+ ret = LLVM.ret++instance Return Bool where+ type Result Bool = LLVM.Value Bool; ret = LLVM.ret+instance Return Int where+ type Result Int = LLVM.Value Int; ret = LLVM.ret+instance Return Int8 where+ type Result Int8 = LLVM.Value Int8; ret = LLVM.ret+instance Return Int16 where+ type Result Int16 = LLVM.Value Int16; ret = LLVM.ret+instance Return Int32 where+ type Result Int32 = LLVM.Value Int32; ret = LLVM.ret+instance Return Int64 where+ type Result Int64 = LLVM.Value Int64; ret = LLVM.ret+instance Return Word where+ type Result Word = LLVM.Value Word; ret = LLVM.ret+instance Return Word8 where+ type Result Word8 = LLVM.Value Word8; ret = LLVM.ret+instance Return Word16 where+ type Result Word16 = LLVM.Value Word16; ret = LLVM.ret+instance Return Word32 where+ type Result Word32 = LLVM.Value Word32; ret = LLVM.ret+instance Return Word64 where+ type Result Word64 = LLVM.Value Word64; ret = LLVM.ret++instance Return Float where+ type Result Float = LLVM.Value Float; ret = LLVM.ret+instance Return Double where+ type Result Double = LLVM.Value Double; ret = LLVM.ret++instance Return (Ptr a) where+ type Result (Ptr a) = LLVM.Value (Ptr a); ret = LLVM.ret+instance (LLVM.IsType a) => Return (LLVM.Ptr a) where+ type Result (LLVM.Ptr a) = LLVM.Value (LLVM.Ptr a); ret = LLVM.ret+instance (LLVM.IsFunction a) => Return (FunPtr a) where+ type Result (FunPtr a) = LLVM.Value (FunPtr a); ret = LLVM.ret+instance Return (StablePtr a) where+ type Result (StablePtr a) = LLVM.Value (StablePtr a); ret = LLVM.ret
src/LLVM/Extra/Memory.hs view
@@ -16,15 +16,12 @@ import qualified LLVM.Extra.Multi.Vector as MultiVector import qualified LLVM.Extra.Multi.Value as MultiValue-import qualified LLVM.Extra.ArithmeticPrivate as A-import qualified LLVM.Extra.Vector as Vector import qualified LLVM.Extra.Scalar as Scalar-import qualified LLVM.Extra.Array as Array import qualified LLVM.Extra.Tuple as Tuple+import qualified LLVM.Extra.Struct as Struct import qualified LLVM.Extra.Either as Either import qualified LLVM.Extra.Maybe as Maybe -import qualified LLVM.Util.Proxy as LP import qualified LLVM.Core as LLVM import LLVM.Core (getElementPtr0,@@ -36,17 +33,13 @@ import qualified Type.Data.Num.Decimal as TypeNum import qualified Type.Data.Num.Unary as Unary import Type.Data.Num.Decimal (d0, d1, d2, d3)-import Type.Base.Proxy (Proxy(Proxy), )--import Foreign.StablePtr (StablePtr, )-import Foreign.Ptr (FunPtr)+import Type.Base.Proxy (Proxy(Proxy)) import qualified Data.Traversable as Trav import qualified Data.Foldable as Fold import qualified Data.FixedLength as FixedLength import Data.Tuple.HT (fst3, snd3, thd3, )-import Data.Word (Word8, Word16, Word32, Word64, Word)-import Data.Int (Int8, Int16, Int32, Int64, )+import Data.Word (Word) import qualified Control.Applicative.HT as App import Control.Monad (ap, (<=<))@@ -237,10 +230,10 @@ Fold.foldlM (\arr (x,i) -> compose x >>= \xc -> LLVM.insertvalue arr xc i) (LLVM.value LLVM.undef) $- FixedLength.zipWith (,) xs $ iterateTrav (1+) (0::Word64)+ FixedLength.zipWith (,) xs $ iterateTrav (1+) (0::Word) decompose arr = Trav.mapM (decompose <=< LLVM.extractvalue arr) $- iterateTrav (1+) (0::Word64)+ iterateTrav (1+) (0::Word) iterateTrav :: (Applicative t, Trav.Traversable t) => (a -> a) -> a -> t a iterateTrav f a0 = snd $ Trav.mapAccumL (\a () -> (f a, a)) a0 $ pure ()@@ -296,114 +289,45 @@ compose = return -class (LLVM.IsFirstClass llvmType, IsType (Stored llvmType)) =>- FirstClass llvmType where- type Stored llvmType :: *- fromStorable :: Value (Stored llvmType) -> CodeGenFunction r (Value llvmType)- toStorable :: Value llvmType -> CodeGenFunction r (Value (Stored llvmType))--instance FirstClass Float where type Stored Float = Float ; fromStorable = return; toStorable = return-instance FirstClass Double where type Stored Double = Double ; fromStorable = return; toStorable = return-instance FirstClass Int where type Stored Int = Int ; fromStorable = return; toStorable = return-instance FirstClass Int8 where type Stored Int8 = Int8 ; fromStorable = return; toStorable = return-instance FirstClass Int16 where type Stored Int16 = Int16 ; fromStorable = return; toStorable = return-instance FirstClass Int32 where type Stored Int32 = Int32 ; fromStorable = return; toStorable = return-instance FirstClass Int64 where type Stored Int64 = Int64 ; fromStorable = return; toStorable = return-instance FirstClass Word where type Stored Word = Word ; fromStorable = return; toStorable = return-instance FirstClass Word8 where type Stored Word8 = Word8 ; fromStorable = return; toStorable = return-instance FirstClass Word16 where type Stored Word16 = Word16 ; fromStorable = return; toStorable = return-instance FirstClass Word32 where type Stored Word32 = Word32 ; fromStorable = return; toStorable = return-instance FirstClass Word64 where type Stored Word64 = Word64 ; fromStorable = return; toStorable = return-instance FirstClass Bool where- type Stored Bool = Word32- fromStorable = A.cmp LLVM.CmpNE (LLVM.value LLVM.zero)- toStorable = LLVM.zext-instance- (TypeNum.Positive n, LLVM.IsPrimitive a, LLVM.IsPrimitive (Stored a), FirstClass a) =>- FirstClass (LLVM.Vector n a) where- type Stored (LLVM.Vector n a) = LLVM.Vector n (Stored a)- fromStorable = Vector.map fromStorable- toStorable = Vector.map toStorable-instance- (TypeNum.Natural n, LLVM.IsFirstClass (Stored a),- FirstClass a, IsSized a, IsSized (Stored a)) =>- FirstClass (LLVM.Array n a) where- type Stored (LLVM.Array n a) = LLVM.Array n (Stored a)- fromStorable = Array.map fromStorable- toStorable = Array.map toStorable--instance (IsType a) => FirstClass (LLVM.Ptr a) where- type Stored (LLVM.Ptr a) = LLVM.Ptr a- fromStorable = return; toStorable = return-instance (LLVM.IsFunction a) => FirstClass (FunPtr a) where- type Stored (FunPtr a) = FunPtr a- fromStorable = return; toStorable = return-instance FirstClass (StablePtr a) where- type Stored (StablePtr a) = StablePtr a- fromStorable = return; toStorable = return-+type family StructStruct s+type instance StructStruct (a,as) = (Struct a, StructStruct as)+type instance StructStruct () = () instance- (LLVM.IsFirstClass (LLVM.Struct s),- IsType (LLVM.Struct (StoredStruct s)),- ConvertStruct s TypeNum.D0 s) =>- FirstClass (LLVM.Struct s) where- type Stored (LLVM.Struct s) = LLVM.Struct (StoredStruct s)- fromStorable sm =- case LP.Proxy of- sfields -> do- s <- decomposeField sfields d0 sm- let _ = asTypeOf (fields s) sfields- return s- toStorable s =- composeField (fields s) d0 s--fields :: Value (LLVM.Struct s) -> LP.Proxy s-fields _ = LP.Proxy---type family StoredStruct s :: *-type instance StoredStruct () = ()-type instance StoredStruct (s,rem) = (Stored s, StoredStruct rem)+ (Struct.Phi s, Struct.Undefined s,+ LLVM.StructFields (StructStruct s),+ ConvertStruct (StructStruct s) TypeNum.D0 s) =>+ C (Struct.T s) where+ type Struct (Struct.T s) = LLVM.Struct (StructStruct s)+ decompose = fmap Struct.Cons . decomposeFields TypeNum.d0+ compose (Struct.Cons s) = composeFields TypeNum.d0 s class ConvertStruct s i rem where- decomposeField ::- LP.Proxy rem -> Proxy i -> Value (LLVM.Struct (StoredStruct s)) ->- CodeGenFunction r (Value (LLVM.Struct s))- composeField ::- LP.Proxy rem -> Proxy i -> Value (LLVM.Struct s) ->- CodeGenFunction r (Value (LLVM.Struct (StoredStruct s)))+ decomposeFields ::+ Proxy i -> Value (LLVM.Struct s) -> CodeGenFunction r rem+ composeFields ::+ Proxy i -> rem -> CodeGenFunction r (Value (LLVM.Struct s)) instance- (sm ~ StoredStruct s,- FirstClass a, am ~ Stored a,- LLVM.GetValue (LLVM.Struct s) (Proxy i),- LLVM.GetValue (LLVM.Struct sm) (Proxy i),- LLVM.ValueType (LLVM.Struct s) (Proxy i) ~ a,- LLVM.ValueType (LLVM.Struct sm) (Proxy i) ~ am,+ (TypeNum.Natural i, LLVM.GetField s i, LLVM.FieldType s i ~ Struct a, C a, ConvertStruct s (TypeNum.Succ i) rem) => ConvertStruct s i (a,rem) where- decomposeField flds i sm = do- s <- decomposeField (fmap snd flds) (decSucc i) sm- a <- fromStorable =<< LLVM.extractvalue sm i- LLVM.insertvalue s a i- composeField flds i s = do- sm <- composeField (fmap snd flds) (decSucc i) s- am <- toStorable =<< LLVM.extractvalue s i+ decomposeFields i sm =+ liftA2 (,)+ (decompose =<< LLVM.extractvalue sm i)+ (decomposeFields (decSucc i) sm)+ composeFields i (a,as) = do+ sm <- composeFields (decSucc i) as+ am <- compose a LLVM.insertvalue sm am i decSucc :: Proxy n -> Proxy (TypeNum.Succ n) decSucc Proxy = Proxy -instance- (sm ~ StoredStruct s,- IsType (LLVM.Struct s),- IsType (LLVM.Struct sm)) =>- ConvertStruct s i () where- decomposeField _ _ _ =- return (LLVM.value LLVM.undef)- composeField _ _ _ =- return (LLVM.value LLVM.undef)+instance (LLVM.StructFields s) => ConvertStruct s i () where+ decomposeFields _ _ = return ()+ composeFields _ _ = return (LLVM.value LLVM.undef)+ instance (MultiValue.C a, C (Tuple.ValueOf a)) => C (MultiValue.T a) where
src/LLVM/Extra/Multi/Value/Private.hs view
@@ -8,6 +8,7 @@ import qualified LLVM.Extra.Arithmetic as A import qualified LLVM.Extra.Control as C import qualified LLVM.Extra.Tuple as Tuple+import qualified LLVM.Extra.Struct as Struct import qualified LLVM.Extra.MaybePrivate as Maybe import qualified LLVM.Core as LLVM@@ -32,7 +33,6 @@ import Data.Complex (Complex((:+))) import Data.Tagged (Tagged(Tagged, unTagged)) import Data.Function (id, (.), ($), )-import Data.Tuple.HT (uncurry3, ) import Data.Maybe (Maybe(Nothing,Just), ) import Data.Bool (Bool(False,True), ) import Data.Word (Word8, Word16, Word32, Word64, Word)@@ -445,24 +445,30 @@ thd3 :: T (a,b,c) -> T c thd3 (Cons (_a,_b,c)) = Cons c +curry3 :: (T (a,b,c) -> d) -> (T a -> T b -> T c -> d)+curry3 f a b c = f $ zip3 a b c +uncurry3 :: (T a -> T b -> T c -> d) -> (T (a,b,c) -> d)+uncurry3 f = TupleHT.uncurry3 f . unzip3++ mapFst3 :: (T a0 -> T a1) -> T (a0,b,c) -> T (a1,b,c)-mapFst3 f = uncurry3 zip3 . TupleHT.mapFst3 f . unzip3+mapFst3 f = TupleHT.uncurry3 zip3 . TupleHT.mapFst3 f . unzip3 mapSnd3 :: (T b0 -> T b1) -> T (a,b0,c) -> T (a,b1,c)-mapSnd3 f = uncurry3 zip3 . TupleHT.mapSnd3 f . unzip3+mapSnd3 f = TupleHT.uncurry3 zip3 . TupleHT.mapSnd3 f . unzip3 mapThd3 :: (T c0 -> T c1) -> T (a,b,c0) -> T (a,b,c1)-mapThd3 f = uncurry3 zip3 . TupleHT.mapThd3 f . unzip3+mapThd3 f = TupleHT.uncurry3 zip3 . TupleHT.mapThd3 f . unzip3 mapFst3F :: (Functor f) => (T a0 -> f (T a1)) -> T (a0,b,c) -> f (T (a1,b,c))-mapFst3F f = fmap (uncurry3 zip3) . FuncHT.mapFst3 f . unzip3+mapFst3F f = fmap (TupleHT.uncurry3 zip3) . FuncHT.mapFst3 f . unzip3 mapSnd3F :: (Functor f) => (T b0 -> f (T b1)) -> T (a,b0,c) -> f (T (a,b1,c))-mapSnd3F f = fmap (uncurry3 zip3) . FuncHT.mapSnd3 f . unzip3+mapSnd3F f = fmap (TupleHT.uncurry3 zip3) . FuncHT.mapSnd3 f . unzip3 mapThd3F :: (Functor f) => (T c0 -> f (T c1)) -> T (a,b,c0) -> f (T (a,b,c1))-mapThd3F f = fmap (uncurry3 zip3) . FuncHT.mapThd3 f . unzip3+mapThd3F f = fmap (TupleHT.uncurry3 zip3) . FuncHT.mapThd3 f . unzip3 zip :: T a -> T b -> T (a,b)@@ -498,6 +504,48 @@ untuple (Cons a) = Cons a +class Struct struct where+ consStruct :: (Struct.T struct ~ a) => a -> T a+ undefStruct :: (Struct.T struct ~ a) => T a+ zeroStruct :: (Struct.T struct ~ a) => T a+ phiStruct :: (Struct.T struct ~ a) =>+ LLVM.BasicBlock -> T a -> LLVM.CodeGenFunction r (T a)+ addPhiStruct :: (Struct.T struct ~ a) =>+ LLVM.BasicBlock -> T a -> T a -> LLVM.CodeGenFunction r ()++instance (Struct struct) => C (Struct.T struct) where+ cons = consStruct+ undef = undefStruct+ zero = zeroStruct+ phi = phiStruct+ addPhi = addPhiStruct++instance Struct () where+ consStruct unit = Cons unit+ undefStruct = Cons (Struct.Cons ())+ zeroStruct = Cons (Struct.Cons ())+ phiStruct _bb = return+ addPhiStruct _bb _a _b = return ()++structCons :: T a -> T (Struct.T as) -> T (Struct.T (a,as))+structCons (Cons b) (Cons (Struct.Cons bs)) = Cons (Struct.Cons (b,bs))++structUncons :: T (Struct.T (a,as)) -> (T a, T (Struct.T as))+structUncons (Cons (Struct.Cons (b,bs))) = (Cons b, Cons (Struct.Cons bs))++instance (C a, Struct as) => Struct (a,as) where+ consStruct (Struct.Cons (a,as)) =+ structCons (cons a) (consStruct (Struct.Cons as))+ undefStruct = structCons undef undefStruct+ zeroStruct = structCons zero zeroStruct+ phiStruct bb at =+ case structUncons at of+ (a,as) -> Monad.lift2 structCons (phi bb a) (phiStruct bb as)+ addPhiStruct bb at bt =+ case (structUncons at, structUncons bt) of+ ((a,as), (b,bs)) -> addPhi bb a b >> addPhiStruct bb as bs++ instance C a => C (Tagged tag a) where cons = tag . cons . unTagged undef = tag undef@@ -648,7 +696,7 @@ instance (Compose a, Compose b, Compose c) => Compose (a,b,c) where type Composed (a,b,c) = (Composed a, Composed b, Composed c)- compose = uncurry3 zip3 . TupleHT.mapTriple (compose, compose, compose)+ compose = TupleHT.uncurry3 zip3 . TupleHT.mapTriple (compose, compose, compose) instance (Decompose pa, Decompose pb, Decompose pc) =>
src/LLVM/Extra/Multi/Vector.hs view
@@ -64,6 +64,8 @@ import qualified Type.Data.Num.Decimal as TypeNum +import qualified Foreign.Storable.Record.Tuple as StoreTuple+ import qualified Data.Traversable as Trav import qualified Data.NonEmpty.Class as NonEmptyC import qualified Data.NonEmpty as NonEmpty@@ -433,6 +435,23 @@ (insert k a0 v0) (insert k a1 v1) (insert k a2 v2)+++instance (C tuple) => C (StoreTuple.Tuple tuple) where+ cons = tuple . cons . fmap StoreTuple.getTuple+ undef = tuple undef+ zero = tuple zero+ phi bb = fmap tuple . phi bb . untuple+ addPhi bb a b = addPhi bb (untuple a) (untuple b)+ shuffle is u v = fmap tuple $ shuffle is (untuple u) (untuple v)+ extract k v = fmap MultiValue.tuple $ extract k (untuple v)+ insert k a v = fmap tuple $ insert k (MultiValue.untuple a) (untuple v)++tuple :: T n tuple -> T n (StoreTuple.Tuple tuple)+tuple (Cons a) = Cons a++untuple :: T n (StoreTuple.Tuple tuple) -> T n tuple+untuple (Cons a) = Cons a class (MultiValue.IntegerConstant a, C a) => IntegerConstant a where
src/LLVM/Extra/Storable/Private.hs view
@@ -439,7 +439,7 @@ update f = MS.StateT $ \a0 -> do a1 <- f a0; return (a0,a1) advancePtrState ::- (C a, Tuple.ValueOf a ~ al, Value (Ptr a) ~ ptr) =>+ (C a, Value (Ptr a) ~ ptr) => MS.StateT ptr (CodeGenFunction r) ptr advancePtrState = update $ advancePtrStatic 1
+ src/LLVM/Extra/Struct.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{- |+In contrast to 'LLVM.Struct' it allows to store high-level values+and thus allows to implement arbitrary-sized tuples of MultiValue's.+-}+module LLVM.Extra.Struct where++import qualified LLVM.Extra.Tuple as Tuple++import qualified LLVM.Core as LLVM++import qualified Control.Applicative.HT as App+import Control.Applicative ((<$>))+++newtype T struct = Cons struct+++class Undefined struct where+ undef :: struct++instance (Undefined struct) => Tuple.Undefined (T struct) where+ undef = Cons undef++instance+ (Tuple.Undefined a, Undefined as) =>+ Undefined (a,as) where+ undef = (Tuple.undef, undef)++instance Undefined () where+ undef = ()+++class Zero struct where+ zero :: struct++instance (Zero struct) => Tuple.Zero (T struct) where+ zero = Cons zero++instance (Tuple.Zero a, Zero as) => Zero (a,as) where+ zero = (Tuple.zero, zero)++instance Zero () where+ zero = ()+++class Phi struct where+ phi :: LLVM.BasicBlock -> struct -> LLVM.CodeGenFunction r struct+ addPhi :: LLVM.BasicBlock -> struct -> struct -> LLVM.CodeGenFunction r ()++instance (Phi struct) => Tuple.Phi (T struct) where+ phi bb (Cons s) = Cons <$> phi bb s+ addPhi bb (Cons a) (Cons b) = addPhi bb a b++instance (Tuple.Phi a, Phi as) => Phi (a,as) where+ phi bb (a,as) = App.lift2 (,) (Tuple.phi bb a) (phi bb as)+ addPhi bb (a,as) (b,bs) = Tuple.addPhi bb a b >> addPhi bb as bs++instance Phi () where+ phi _bb = return+ addPhi _bb () () = return ()+++class (Undefined (ValueOf struct)) => Value struct where+ type ValueOf struct+ valueOf :: struct -> ValueOf struct++instance (Value struct) => Tuple.Value (T struct) where+ type ValueOf (T struct) = T (ValueOf struct)+ valueOf (Cons struct) = Cons $ valueOf struct++instance (Tuple.Value a, Value as) => Value (a,as) where+ type ValueOf (a,as) = (Tuple.ValueOf a, ValueOf as)+ valueOf (a,as) = (Tuple.valueOf a, valueOf as)++instance Value () where+ type ValueOf () = ()+ valueOf () = ()