Matrices de tipo seguro en Haskell

Las matrices seguras de tipos son un tema constante. Discuten sobre su relevancia, y se escriben lenguajes completos para implementar listas con una longitud a nivel de tipo . Me pareció extraño que todavía no haya ninguna variante en Haskell que cumpla con los criterios cuerdos de conveniencia y seguridad. ¿Hay alguna razón para la falta de bibliotecas listas para usar, o simplemente no son necesarias? Vamos a averiguarlo.



La manera más segura de entender por qué algo (¡que ciertamente debería serlo!) No lo es, es intentar hacerlo usted mismo. Intentemos ..



Expectativa



La primera cosa que viene a la mente (al menos para mí) artículo sobre la Haskell nivel de tipo , donde, con la ayuda de DataKinds, GADTs, KindSignatures(una descripción breve de dónde y por qué se usan - abajo) se introducen los números naturales inductivas, y detrás de ellos y vectores parametrizar longitud:



data Nat = Zero | Succ Nat

data Vector (n :: Nat) a where
  (:|) :: a -> Vector n a -> Vector ('Succ n) a
  Nil :: Vector 'Zero a

infixr 3 :| 


KindSignaturesse usa para indicar que npuede no ser cualquier tipo con un tipo *(como un parámetro en el mismo ejemplo), sino un valor de tipo Nat elevado al nivel de tipos. Esto es posible por extensión DataKinds. GADTsson necesarios para que el constructor pueda influir en el tipo de valor. En nuestro caso, el constructor Nilconstruirá exactamente el Vector de longitud Zero, y el constructor :|adjuntará un elemento de tipo al vector en el segundo argumento ay aumentará el tamaño en uno. Para obtener una descripción más detallada y correcta, puede leer el artículo al que me referí anteriormente o la Wiki de Haskell.



Qué. Esto parece ser lo que necesitamos. Solo queda ingresar a la matriz:



newtype Matrix (m :: Nat) (n :: Nat) a = Matrix (Vector m (Vector n a))


Y esto incluso funcionará:



>>> :t Matrix $ (1 :| Nil) :| Nil
Matrix $ (1 :| Nil) :| Nil :: Num a => Matrix ('Succ 'Zero) ('Succ 'Zero) a

>>> :t Matrix $ (1 :| 2 :| Nil) :| (3 :| 4 :| Nil) :| Nil
Matrix $ (1 :| 2 :| Nil) :| (3 :| 4 :| Nil) :| Nil
  :: Num a => Matrix ('Succ ('Succ 'Zero)) ('Succ ('Succ 'Zero)) a


Los problemas de este enfoque ya están surgiendo de todas las grietas, pero puedes vivir con ellos, continuaremos.



, , , , , :



(*|) :: Num a => a -> Matrix m n a -> Matrix m n a
(*|) n = fmap (n *)

--        fmap
--       

instance Functor (Matrix m n) where
  fmap f (Matrix vs) = Matrix $ fmap f <$> vs

instance Functor (Vector n) where
  fmap f (v :| vs) = (f v) :| (fmap f vs)
  fmap _ Nil = Nil


, :



>>> :t fmap (+1) (1 :| 2 :| Nil)
fmap (+1) (1 :| 2 :| Nil)
  :: Num b => Vector ('Succ ('Succ 'Zero)) b

>>> fmap  (+1) (1 :| 2 :| Nil)
2 :| 3 :| Nil

λ > :t 5 *| Matrix ((1 :| 2 :| Nil) :| ( 3 :| 4 :| Nil) :| Nil)
5 *| Matrix ((1 :| 2 :| Nil) :| ( 3 :| 4 :| Nil) :| Nil)
  :: Num a => Matrix ('Succ ('Succ 'Zero)) ('Succ ('Succ 'Zero)) a

λ > 5 *| Matrix ((1 :| 2 :| Nil) :| ( 3 :| 4 :| Nil) :| Nil)
Matrix 5 :| 10 :| Nil :| 15 :| 20 :| Nil :| Nil


:



zipVectorWith :: (a -> b -> c) -> Vector n a -> Vector n b -> Vector n c
zipVectorWith f (l:|ls) (v:|vs) = f l v :| (zipVectorWith f ls vs)
zipVectorWith _ Nil Nil = Nil

(|+|) :: Num a => Matrix m n a -> Matrix m n a -> Matrix m n a
(|+|) (Matrix l) (Matrix r) = Matrix $ zipVectorWith (zipVectorWith (+)) l r


: , , . , :




-- transpose               :: [[a]] -> [[a]]
-- transpose []             = []
-- transpose ([]   : xss)   = transpose xss
-- transpose ((x:xs) : xss) = (x : [h | (h:_) <- xss]) : transpose (xs : [ t | (_:t) <- xss])

transposeMatrix :: Vector m (Vector n a) -> Vector n (Vector m a)
transposeMatrix Nil = Nil
transposeMatrix ((x:|xs):|xss) = (x :| (fmap headVec xss)) :| (transposeMatrix (xs :| fmap tailVec xss))


, GHC ( ).



Could not deduce: n ~ 'Zero
      from the context: m ~ 'Zero
        bound by a pattern with constructor:
                   Nil :: forall a. Vector 'Zero a,
                 in an equation for ‘transposeMatrix’
        at Text.hs:42:17-19
      ‘n’ is a rigid type variable bound by
        the type signature for:
          transposeMatrix :: forall (m :: Nat) (n :: Nat) a.
                             Vector m (Vector n a) -> Vector n (Vector m a)
        at Text.hs:41:1-79
      Expected type: Vector n (Vector m a)
        Actual type: Vector 'Zero (Vector m a)In the expression: Nil
      In an equation for ‘transposeMatrix’: transposeMatrix Nil = NilRelevant bindings include
        transposeMatrix :: Vector m (Vector n a) -> Vector n (Vector m a)
          (bound at Text.hs:42:1)
   |
   | transposeMatrix Nil = Nil
   |


? , m Zero n Zero.

, , e Nil, Nil' n. n , , n .





, , - , . Haskell , .



- . . ?



Haskell : linear laop, :



  • linear
  • laop


linear laop. ? , , : , Succ Zero:



Matrix $ (1 :| 2 :| 3 :| 4 :| Nil) :| (5 :| 6 :| 7 :| 8 :| Nil) :| (9 :| 10 :| 11 :| Nil) :| Nil


Couldn't match type ‘'Zero’ with ‘'Succ 'Zero
      Expected type: Vector
                       ('Succ 'Zero) (Vector ('Succ ('Succ ('Succ ('Succ 'Zero)))) a)
        Actual type: Vector
                       ('Succ 'Zero) (Vector ('Succ ('Succ ('Succ 'Zero))) a)In the second argument of ‘(:|)’, namely
        ‘(9 :| 10 :| 11 :| Nil) :| NilIn the second argument of ‘(:|)’, namely
        ‘(5 :| 6 :| 7 :| 8 :| Nil) :| (9 :| 10 :| 11 :| Nil) :| NilIn the second argument of ‘($)’, namely
        ‘(1 :| 2 :| 3 :| 4 :| Nil)
           :| (5 :| 6 :| 7 :| 8 :| Nil) :| (9 :| 10 :| 11 :| Nil) :| Nil


, GHC, - . ?



Template Haskell



TemplateHaskell (TH) — , -, , . .



matlab:



v = [1 2 3]
m = [1 2 3; 4 5 6; 7 8 10]


:



matrix := line; line | line
line := unit units
units := unit | ε
unit := var | num | inside_brackets




  • var —
  • num — ( )
  • inside_brackets — Haskell ( ). Haskell haskell-src-exts haskell-src-meta


( , !). :



matrix :: Parser [[Exp]]
matrix = (line `sepBy` char ';') <* eof

line :: Parser [Exp]
line = spaces >> unit `endBy1` spaces

unit :: Parser Exp
unit = (var <|> num <|> inBrackets) >>= toExpr


Exp — AST Haskell, , ( AST ).



c , ,



data Matrix (m :: Nat) (n :: Nat) a where
  Matrix :: forall m n a. (Int, Int) -> ![[a]] -> Matrix m n a


, AST



expr :: Parser.Parser [[Exp]] -> String -> Q Exp
expr parser source = do -- parser    matrix   
  --      
  let (matrix, (m, n)) = unwrap $ parse source parser 
  --  AST
  let sizeType = LitT . NumTyLit
  --  TypeApplication  ,       ,        
  let constructor = foldl AppTypeE (ConE 'Matrix) [sizeType m, sizeType n, WildCardT]
  let size = TupE $ map (LitE . IntegerL) [m, n]
  let value = ListE $ map ListE $ matrix
  pure $ foldl AppE constructor [size, value]

parse :: String -> Parser.Parser [[a]] -> Either [String] ([[a]], (Integer, Integer))
parse source parser = do
  matrix <- Parser.parse parser "QLinear" source --   
  size <- checkSize matrix --  
  pure (matrix, size)


: QuasiQuoter



matrix :: QuasiQuoter
matrix =
  QuasiQuoter
    { quoteExp = expr Parser.matrix,
      quotePat = notDefined "Pattern",
      quoteType = notDefined "Type",
      quoteDec = notDefined "Declaration"
    }


! :



>>> :set -XTemplateHaskell -XDataKinds -XQuasiQuotes -XTypeApplications
>>> :t [matrix| 1 2; 3 4 |]
[matrix| 1 2; 3 4 |] :: Num _ => Matrix 2 2 _

>>> :t [matrix| 1 2; 3 4 5 |]
<interactive>:1:1: error:Exception when trying to run compile-time code:
        All lines must be the same length
CallStack (from HasCallStack):
  error, called at src/Internal/Quasi/Quasi.hs:9:19 in qlnr-0.1.2.0-82f1f55c:Internal.Quasi.Quasi
      Code: template-haskell-2.15.0.0:Language.Haskell.TH.Quote.quoteExp
              matrix " 1 2; 3 4 5 "In the quasi-quotation: [matrix| 1 2; 3 4 5 |]

>>> :t [matrix| (length . show); (+1) |]
[matrix| (length . show); (+1) |] :: Matrix 2 1 (Int -> Int)


TH , c . , hackage



>>> [operator| (x, y) => (y, x) |]
[0,1]
[1,0]
>>> [operator| (x, y) => (2 * x, y + x) |] ~*~ [vector| 3 4 |]
[6]
[7]




Haskell , . ? . , ( ), .



, . : .



Enlaces duplicados al repositorio y piratería



Los comentarios, sugerencias y solicitudes de extracción son bienvenidos.




All Articles