1

I am trying to have precomputed data embedded in Haskell. That is

catToMap li = Map.fromList $ zip [0..] li

cat1 = catToMap ["aa", "bb", "cc"]

dim = Map.size cat1

I would like to use dim statically in a type definition:

type Network = Grenade.Network
    '[Grenade.FullyConnected dim 20, Grenade.FullyConnected 20 1, Grenade.Logit]
    '[Grenade.D1 dim, Grenade.D1 20, Grenade.D1 1, Grenade.D1 1]

(imported from grenade library)

However, the above gives the error that dim is not in scope.

I am also trying to create the function

import qualified Numeric.LinearAlgebra.Static as SA

-- | ith standard basis in Rn
stdbasis :: forall n . KnownNat n => Int -> SA.R n
stdbasis i = SA.vector [builder x| x <- [0..n-1]]
where
    builder j = if i == j then 1 else 0

but this gives me the error that n is not in scope.

My attempt to fix the first problem is with template Haskell:

catToMap = $(\li -> Map.fromList $ zip [0..] li)

cat1 = $(catToMap ["aa", "bb", "cc"])

dim = $(Map.size cat1)

but it gives me the error

• Couldn't match expected type ‘Q Exp’
              with actual type ‘[a0] -> Map.Map Integer a0’
• The lambda expression ‘\ li -> (Map.fromList $ zip ... li)’
  has one argument,
  but its type ‘Language.Haskell.TH.Lib.ExpQ’ has none
  In the expression: \ li -> (Map.fromList $ zip [0 .. ] li)
  In the untyped splice: $(\ li -> (Map.fromList $ zip [0 .. ] li))

What I am trying to achieve is similar to C++ templates:

template <int size> Vector<n>
stdbasis(int i);
Henricus V.
  • 898
  • 1
  • 8
  • 29
  • Well for the second error, `n` is indeed not in scope, you should use `[0..i-1]`. Regardless of that, I do not see much benefit for this, since the constants will typically be calculated once, due to lazy programming anyway. – Willem Van Onsem Jun 17 '18 at 14:41
  • @WillemVanOnsem If I manually type in the value for `dim` every time, it is error prone and difficult to change (say if a new category is added). But that value is needed at compile time for constructing `Network` – Henricus V. Jun 17 '18 at 14:46

1 Answers1

1

Template Haskell is the correct approach, but you need to use it at the right spot and with a suitable TH constructor. The spot is where you want to use a type-level quantity that's dependent on a value-level computation. dim is still value-level, but the argument of Grenade.FullyConnected is type-level, so that's where you need the splice.

Here's a full (simplified) example:

module Dimension where  -- No TH here, we just need a separate module
                        -- to put the value-level code whose results
                        -- are to be spliced into the type level
import Data.Map as Map

dim :: Integer
dim = fromIntegral $ Map.size cat1
 where catToMap li = Map.fromList $ zip [0..] li
       cat1 = catToMap ["aa", "bb", "cc"]
{-# LANGUAGE TemplateHaskell, TypeOperators, DataKinds #-}

module Main where    

import Data.Modular
import Dimension
import Language.Haskell.TH

type F = ℤ / $(pure . LitT $ NumTyLit dim) -- this is where you'd define your Network type

main :: IO ()
main = print [0::F ..]
[0,1,2]
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319