tup-functor (empty) → 0.1
raw patch · 9 files changed
+560/−0 lines, 9 filesdep +basedep +haskell-src-extsdep +parsec2setup-changed
Dependencies added: base, haskell-src-exts, parsec2
Files
- Data/Tup.hs +23/−0
- Data/Tup/Class.hs +58/−0
- Data/Tup/Lazy.hs +9/−0
- Data/Tup/Strict.hs +9/−0
- Data/Tup/Tup.inc +233/−0
- LICENSE +29/−0
- Setup.lhs +3/−0
- preprocessor/tpp.hs +154/−0
- tup-functor.cabal +42/−0
+ Data/Tup.hs view
@@ -0,0 +1,23 @@+ +-- | This small library defines data types 'Tup1', 'Tup2' ... 'Tup9' for homogeneous tuples of small size (both strict and lazy), +-- and various instances for them, most notably 'Functor' and 'Applicative'. We also have a 'Tup' type class: +-- +-- > class Tup f where +-- > tupSize :: f a -> Int +-- > tupToList :: f a -> [a] +-- > tupFromList :: [a] -> f a +-- > tupUndef :: f a -> a +-- +-- Also included is a very simple preprocesszor @tpp@ which translates +-- the syntax @\{\{a,b,c\}\}@ into @(Tup3 a b c)@. + +module Data.Tup + ( module Data.Tup.Class + , module Data.Tup.Lazy + ) + where + +import Control.Applicative ( Applicative ) -- only for Haddock + +import Data.Tup.Class +import Data.Tup.Lazy
+ Data/Tup/Class.hs view
@@ -0,0 +1,58 @@+ +module Data.Tup.Class where + +-------------------------------------------------------------------------------- + +import Control.Applicative +import Data.List + +-------------------------------------------------------------------------------- +-- * the Tup class + +class (Functor f, Applicative f) => Tup f where + tupSize :: f a -> Int + tupToList :: f a -> [a] + tupFromList :: [a] -> f a + + tupUndef :: f a -> a + tupUndef = undefined + +-------------------------------------------------------------------------------- +-- * misc + +-- | Safe version of 'tupFromList'. +maybeTupFromList :: Tup f => [a] -> Maybe (f a) +maybeTupFromList xs = result where + result = if length xs == tupSize (undef result) + then Just (tupFromList xs) + else Nothing + undef :: Maybe a -> a + undef _ = undefined + +-- | Transpose a Tup of Tups. +transposeTup :: (Tup f, Tup g) => f (g a) -> g (f a) +transposeTup = tupFromList . (map tupFromList) . transpose . (map tupToList) . tupToList + +-------------------------------------------------------------------------------- +-- * zipping + +zipTupWith :: Applicative f => (a -> b -> c) -> f a -> f b -> f c +zipTupWith f t1 t2 = f <$> t1 <*> t2 + +zipTupWith3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d +zipTupWith3 f t1 t2 t3 = f <$> t1 <*> t2 <*> t3 + +zipTupWith4 :: Applicative f => (a -> b -> c -> d -> e) -> f a -> f b -> f c -> f d -> f e +zipTupWith4 f t1 t2 t3 t4 = f <$> t1 <*> t2 <*> t3 <*> t4 + +zipTup :: Applicative f => f a -> f b -> f (a,b) +zipTup t1 t2 = (,) <$> t1 <*> t2 + +zipTup3 :: Applicative f => f a -> f b -> f c -> f (a,b,c) +zipTup3 t1 t2 t3 = (,,) <$> t1 <*> t2 <*> t3 + +zipTup4 :: Applicative f => f a -> f b -> f c -> f d -> f (a,b,c,d) +zipTup4 t1 t2 t3 t4 = (,,,) <$> t1 <*> t2 <*> t3 <*> t4 + +-------------------------------------------------------------------------------- +
+ Data/Tup/Lazy.hs view
@@ -0,0 +1,9 @@+ +{-# LANGUAGE CPP, DeriveFunctor, DeriveFoldable, DeriveTraversable #-} + +module Data.Tup.Lazy where + +#define A a + +#include "Tup.inc" +
+ Data/Tup/Strict.hs view
@@ -0,0 +1,9 @@+ +{-# LANGUAGE CPP, DeriveFunctor, DeriveFoldable, DeriveTraversable #-} + +module Data.Tup.Strict where + +#define A !a + +#include "Tup.inc" +
+ Data/Tup/Tup.inc view
@@ -0,0 +1,233 @@+ +-------------------------------------------------------------------------------- + +import Control.Applicative + +import Data.List +import Data.Foldable +import Data.Traversable +import Data.Monoid + +import Foreign.Ptr +import Foreign.Storable +import Foreign.Marshal + +import Data.Tup.Class + +-------------------------------------------------------------------------------- +-- * data type declarations + +data Tup1 a = Tup1 A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup2 a = Tup2 A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup3 a = Tup3 A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup4 a = Tup4 A A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup5 a = Tup5 A A A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup6 a = Tup6 A A A A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup7 a = Tup7 A A A A A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup8 a = Tup8 A A A A A A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) +data Tup9 a = Tup9 A A A A A A A A A deriving (Eq,Ord,Show,Read,Functor,Foldable,Traversable) + +-------------------------------------------------------------------------------- +-- * \"tupping\" + +tupTup :: Applicative f => f a -> f a -> f (Tup2 a) +tupTup t1 t2 = Tup2 <$> t1 <*> t2 + +tupTup3 :: Applicative f => f a -> f a -> f a -> f (Tup3 a) +tupTup3 t1 t2 t3 = Tup3 <$> t1 <*> t2 <*> t3 + +tupTup4 :: Applicative f => f a -> f a -> f a -> f a -> f (Tup4 a) +tupTup4 t1 t2 t3 t4 = Tup4 <$> t1 <*> t2 <*> t3 <*> t4 + +tupTup5 :: Applicative f => f a -> f a -> f a -> f a -> f a -> f (Tup5 a) +tupTup5 t1 t2 t3 t4 t5 = Tup5 <$> t1 <*> t2 <*> t3 <*> t4 <*> t5 + +-------------------------------------------------------------------------------- +-- * instances + +instance Tup Tup1 where + tupSize _ = 1 + tupToList (Tup1 x1) = [x1] + tupFromList [x1] = Tup1 x1 + tupFromList _ = error "tupFromList: list should have length 1" + +instance Tup Tup2 where + tupSize _ = 2 + tupToList (Tup2 x1 x2) = [x1,x2] + tupFromList [x1,x2] = Tup2 x1 x2 + tupFromList _ = error "tupFromList: list should have length 2" + +instance Tup Tup3 where + tupSize _ = 3 + tupToList (Tup3 x1 x2 x3) = [x1,x2,x3] + tupFromList [x1,x2,x3] = Tup3 x1 x2 x3 + tupFromList _ = error "tupFromList: list should have length 3" + +instance Tup Tup4 where + tupSize _ = 4 + tupToList (Tup4 x1 x2 x3 x4) = [x1,x2,x3,x4] + tupFromList [x1,x2,x3,x4] = Tup4 x1 x2 x3 x4 + tupFromList _ = error "tupFromList: list should have length 4" + +instance Tup Tup5 where + tupSize _ = 5 + tupToList (Tup5 x1 x2 x3 x4 x5) = [x1,x2,x3,x4,x5] + tupFromList [x1,x2,x3,x4,x5] = Tup5 x1 x2 x3 x4 x5 + tupFromList _ = error "tupFromList: list should have length 5" + +instance Tup Tup6 where + tupSize _ = 6 + tupToList (Tup6 x1 x2 x3 x4 x5 x6) = [x1,x2,x3,x4,x5,x6] + tupFromList [x1,x2,x3,x4,x5,x6] = Tup6 x1 x2 x3 x4 x5 x6 + tupFromList _ = error "tupFromList: list should have length 6" + +instance Tup Tup7 where + tupSize _ = 7 + tupToList (Tup7 x1 x2 x3 x4 x5 x6 x7) = [x1,x2,x3,x4,x5,x6,x7] + tupFromList [x1,x2,x3,x4,x5,x6,x7] = Tup7 x1 x2 x3 x4 x5 x6 x7 + tupFromList _ = error "tupFromList: list should have length 7" + +instance Tup Tup8 where + tupSize _ = 8 + tupToList (Tup8 x1 x2 x3 x4 x5 x6 x7 x8) = [x1,x2,x3,x4,x5,x6,x7,x8] + tupFromList [x1,x2,x3,x4,x5,x6,x7,x8] = Tup8 x1 x2 x3 x4 x5 x6 x7 x8 + tupFromList _ = error "tupFromList: list should have length 8" + +instance Tup Tup9 where + tupSize _ = 9 + tupToList (Tup9 x1 x2 x3 x4 x5 x6 x7 x8 x9) = [x1,x2,x3,x4,x5,x6,x7,x8,x9] + tupFromList [x1,x2,x3,x4,x5,x6,x7,x8,x9] = Tup9 x1 x2 x3 x4 x5 x6 x7 x8 x9 + tupFromList _ = error "tupFromList: list should have length 9" + +-------------------------------------------------------------------------------- + +instance Applicative Tup1 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup1 x + Tup1 f1 <*> Tup1 x1 = Tup1 (f1 x1) + +instance Applicative Tup2 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup2 x x + Tup2 f1 f2 <*> Tup2 x1 x2 = Tup2 (f1 x1) (f2 x2) + +instance Applicative Tup3 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup3 x x x + Tup3 f1 f2 f3 <*> Tup3 x1 x2 x3 = Tup3 (f1 x1) (f2 x2) (f3 x3) + +instance Applicative Tup4 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup4 x x x x + Tup4 f1 f2 f3 f4 <*> Tup4 x1 x2 x3 x4 = Tup4 (f1 x1) (f2 x2) (f3 x3) (f4 x4) + +instance Applicative Tup5 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup5 x x x x x + Tup5 f1 f2 f3 f4 f5 <*> Tup5 x1 x2 x3 x4 x5 = Tup5 (f1 x1) (f2 x2) (f3 x3) (f4 x4) (f5 x5) + +instance Applicative Tup6 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup6 x x x x x x + Tup6 f1 f2 f3 f4 f5 f6 <*> Tup6 x1 x2 x3 x4 x5 x6 = Tup6 (f1 x1) (f2 x2) (f3 x3) (f4 x4) (f5 x5) (f6 x6) + +instance Applicative Tup7 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup7 x x x x x x x + Tup7 f1 f2 f3 f4 f5 f6 f7 <*> Tup7 x1 x2 x3 x4 x5 x6 x7 + = Tup7 (f1 x1) (f2 x2) (f3 x3) (f4 x4) (f5 x5) (f6 x6) (f7 x7) + +instance Applicative Tup8 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup8 x x x x x x x x + Tup8 f1 f2 f3 f4 f5 f6 f7 f8 <*> Tup8 x1 x2 x3 x4 x5 x6 x7 x8 + = Tup8 (f1 x1) (f2 x2) (f3 x3) (f4 x4) (f5 x5) (f6 x6) (f7 x7) (f8 x8) + +instance Applicative Tup9 where + {-# INLINE pure #-} + {-# INLINE (<*>) #-} + pure x = Tup9 x x x x x x x x x + Tup9 f1 f2 f3 f4 f5 f6 f7 f8 f9 <*> Tup9 x1 x2 x3 x4 x5 x6 x7 x8 x9 + = Tup9 (f1 x1) (f2 x2) (f3 x3) (f4 x4) (f5 x5) (f6 x6) (f7 x7) (f8 x8) (f9 x9) + +-------------------------------------------------------------------------------- + +#define NUM_INSTANCE \ + { t1 + t2 = (+) <$> t1 <*> t2 \ + ; t1 - t2 = (-) <$> t1 <*> t2 \ + ; t1 * t2 = (*) <$> t1 <*> t2 \ + ; abs = fmap abs \ + ; signum = fmap signum \ + ; fromInteger = pure . fromInteger } + +instance Num a => Num (Tup1 a) where NUM_INSTANCE +instance Num a => Num (Tup2 a) where NUM_INSTANCE +instance Num a => Num (Tup3 a) where NUM_INSTANCE +instance Num a => Num (Tup4 a) where NUM_INSTANCE +instance Num a => Num (Tup5 a) where NUM_INSTANCE +instance Num a => Num (Tup6 a) where NUM_INSTANCE +instance Num a => Num (Tup7 a) where NUM_INSTANCE +instance Num a => Num (Tup8 a) where NUM_INSTANCE +instance Num a => Num (Tup9 a) where NUM_INSTANCE + +-------------------------------------------------------------------------------- + +#define FRACTIONAL_INSTANCE \ + { t1 / t2 = (/) <$> t1 <*> t2 \ + ; recip = fmap recip \ + ; fromRational = pure . fromRational } + +instance Fractional a => Fractional (Tup1 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup2 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup3 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup4 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup5 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup6 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup7 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup8 a) where FRACTIONAL_INSTANCE +instance Fractional a => Fractional (Tup9 a) where FRACTIONAL_INSTANCE + +-------------------------------------------------------------------------------- + +#define MONOID_INSTANCE \ + { mempty = pure mempty \ + ; mappend t1 t2 = mappend <$> t1 <*> t2 } + +instance Monoid a => Monoid (Tup1 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup2 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup3 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup4 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup5 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup6 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup7 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup8 a) where MONOID_INSTANCE +instance Monoid a => Monoid (Tup9 a) where MONOID_INSTANCE + +-------------------------------------------------------------------------------- + +#define STORABLE_INSTANCE \ + { sizeOf t = tupSize t * sizeOf (tupUndef t) \ + ; alignment t = alignment (tupUndef t) \ + ; peek ptr = let { ptrUndef :: Ptr b -> b ; ptrUndef _ = undefined } \ + in tupFromList <$> peekArray (tupSize $ ptrUndef ptr) (castPtr ptr) \ + ; poke ptr t = pokeArray (castPtr ptr) (tupToList t) } + +instance Storable a => Storable (Tup1 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup2 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup3 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup4 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup5 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup6 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup7 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup8 a) where STORABLE_INSTANCE +instance Storable a => Storable (Tup9 a) where STORABLE_INSTANCE + +--------------------------------------------------------------------------------
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2012, Balazs Komuves+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+this list of conditions and the following disclaimer.+ +- Redistributions in binary form must reproduce the above copyright notice,+this list of conditions and the following disclaimer in the documentation+and/or other materials provided with the distribution.+ +- Neither names of the copyright holders nor the names of the contributors+may be used to endorse or promote products derived from this software without+specific prior written permission. ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ preprocessor/tpp.hs view
@@ -0,0 +1,154 @@+ +-- | Tuple brackets preprocessor. +-- Use like this: +-- +-- > {-# OPTIONS -F -pgmF tuplepp #-} +-- +-- Write code like this: +-- +-- > x = {{a,b,c}} +-- +-- and it will be translated to this: +-- +-- > x = Tup3 a b c +-- +{-# LANGUAGE PackageImports #-} +module Main where + +-------------------------------------------------------------------------------- + +import Data.Char + +import Language.Haskell.Exts +import "parsec2" Text.ParserCombinators.Parsec + +import System.Environment + +-- import Debug.Trace +-- debug x = trace (show x) + +-------------------------------------------------------------------------------- + +tupleCon :: Int -> Exp +tupleCon k = Var $ UnQual $ Ident ("Tup" ++ show k) + +toTupleE :: Exp -> Exp +toTupleE e = case e of + Tuple es -> foldl App (tupleCon (length es)) es + _ -> App (tupleCon 1) e + +inparens :: String -> String +inparens s = "(" ++ s ++ ")" + +toTupleS :: String -> String +toTupleS s = {- trace ("|"++s++"|") $ -} case parseExp (inparens s) of + ParseOk e -> pp (toTupleE e) + err -> error ("parse error in idiom bracket:\n" ++ show err) + +pp :: Pretty a => a -> String +pp = prettyPrintStyleMode style mode where + mode = defaultMode + { layout = PPNoLayout + } + style = Style + { mode = LeftMode -- OneLineMode + , lineLength = 1000 + , ribbonsPerLine = 1.5 + } + +-------------------------------------------------------------------------------- + +data TupleLine + = S String + | I TupleLine (Bool,TupleLine) TupleLine -- pre (isTuple,bracket) post + deriving Show + +{- +xinit :: TupleLine -> TupleLine +xinit (S str) = S (init str) +xinit (I pre mid post) = I pre mid (xinit post) + +xtail :: TupleLine -> TupleLine +xtail (S str) = S (tail str) +xtail (I pre mid post) = I (xtail pre) mid post +-} + +lineP :: Parser TupleLine +lineP = do + pre <- many (noneOf "{}") + m <- optionMaybe $ do + + char '{' + c <- lookAhead anyChar + mid <- case c of + '{' -> char '{' >> lineP + _ -> lineP + + char '}' + d <- lookAhead anyChar + post <- case d of + '}' -> char '}' >> lineP + _ -> lineP + + let b = (c=='{') && (d=='}') -- True -- if isAlphaNum c || elem c "({[_" then True else False + + return (b,mid,post) + return $ case m of + Nothing -> S pre + Just (b,mid,post) -> case b of + True -> I (S pre) (b,mid) (post) + False -> I (S pre) (b,mid) (post) + +parseLine :: SourceName -> String -> TupleLine +parseLine src s = case runParser lineP () "" s of + Right xx -> xx + Left err -> error ("preprocessor parse error in file \"" ++ src ++ "\":\n" ++ show err) + +convertTupleLine :: TupleLine -> String +convertTupleLine (S ln) = ln +convertTupleLine (I pre (b,mid) post) = pre' ++ mid'' ++ post' where + pre' = convertTupleLine pre + mid' = convertTupleLine mid + post' = convertTupleLine post + mid'' = if b + then '(' : toTupleS mid' ++ ")" + else "{" ++ mid' ++ "}" + +processLine :: SourceName -> String -> String +processLine src = convertTupleLine . parseLine src + +-------------------------------------------------------------------------------- + +main :: IO () +main = + do + args <- getArgs + case args of + + [] -> interact (f "stdin") + [inp] -> do + text <- readFile inp + putStrLn $ f inp text + [inp,out] -> do + text <- readFile inp + writeFile out $ f inp text + [inp,cpp,out] -> do -- ghc calls the preprocessor this way + text <- readFile cpp + let pptext = f cpp text + writeFile out pptext + + _ -> do + print args + putStrLn $ unlines + [ "usage:" + , " tpp <input.hs >output.hs" + , " tpp input.hs >output.hs" + , " tpp input.hs output.hs" + , " tpp dummy.hs input.hs output.hs" + ] + + where + f src text = processLine src text + +-------------------------------------------------------------------------------- +
+ tup-functor.cabal view
@@ -0,0 +1,42 @@+ +Name: tup-functor +Version: 0.1 +Synopsis: Homogeneous tuples +Description: Homogeneous tuples (also known as vectors), with various instances, most notably 'Functor' and 'Applicative'. + The primary goal of the library is to help functor-oriented programming + (for low-dimensional linear algebra, there are more specific packages, eg. @vect@). + A small preprocessor for a tuple syntax is also included. +Author: Balazs Komuves +license: BSD3 +license-file: LICENSE +Copyright: (c) 2012 Balazs Komuves +Maintainer: bkomuves (plus) hackage (at) gmail (dot) hu +Stability: Experimental +Category: System +Tested-With: GHC == 7.0.3 +Cabal-Version: >= 1.6 +Build-Type: Simple + +extra-source-files: Data/Tup/Tup.inc + +Library + Build-Depends: base >= 3 && < 5 + + Exposed-Modules: Data.Tup + Data.Tup.Class + Data.Tup.Strict + Data.Tup.Lazy + + Extensions: CPP + Hs-Source-Dirs: . + + -- force-recomp is necessary since all the source code is included via the C preprocessor, + -- and thus the recompilation checker is broken + ghc-options: -fforce-recomp -Wall -fno-warn-unused-matches -fno-warn-unused-imports -fno-warn-unused-binds + +Executable tuplepp + Build-Depends: base >= 3 && < 5, parsec2, haskell-src-exts + Main-is: preprocessor/tpp.hs + Hs-Source-Dirs: . + ghc-options: -Wall -fno-warn-unused-matches -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-unused-do-bind +