developer tip

n 차원 그리드 유형에 대한 cojoin 또는 cobind 작성

copycodes 2020. 12. 31. 22:15
반응형

n 차원 그리드 유형에 대한 cojoin 또는 cobind 작성


유형 수준 내추럴의 일반적인 정의를 사용하여 n 차원 그리드를 정의했습니다.

{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}

data Nat = Z | S Nat

data U (n :: Nat) x where
  Point :: x -> U Z x
  Dimension :: [U n x] -> U n x -> [U n x] -> U (S n) x

dmap :: (U n x -> U m r) -> U (S n) x -> U (S m) r
dmap f (Dimension ls mid rs) = Dimension (map f ls) (f mid) (map f rs)

instance Functor (U n) where
  fmap f (Point x) = Point (f x)
  fmap f d@Dimension{} = dmap (fmap f) d

이제 나는 그것을 Comonad의 인스턴스로 만들고 싶지만 내 뇌를 감싸지 못합니다.

class Functor w => Comonad w where
  (=>>)    :: w a -> (w a -> b) -> w b
  coreturn :: w a -> a
  cojoin   :: w a -> w (w a)

  x =>> f = fmap f (cojoin x)
  cojoin xx = xx =>> id

instance Comonad (U n) where
  coreturn (Point x) = x
  coreturn (Dimension _ mid _) = coreturn mid

  -- cojoin :: U Z x -> U Z (U Z x)
  cojoin (Point x) = Point (Point x)
  -- cojoin ::U (S n) x -> U (S n) (U (S n) x)
  cojoin d@Dimension{} = undefined

  -- =>> :: U Z x -> (U Z x -> r) -> U Z r
  p@Point{} =>> f = Point (f p)
  -- =>> :: U (S n) x -> (U (S n) x -> r) -> U (S n) r
  d@Dimension{} =>> f = undefined

cojoinn 차원 그리드에서 사용 하면 n 차원 그리드의 n 차원 그리드가 생성됩니다. I는 동일한 아이디어 인스턴스를 제공하려는 이와 점이다 cojoined (X, Y, Z)에 그리드가 있어야 원래 격자 중심 (X, Y, Z)에. 이 코드를 수정하려면 "fmaps"및 "rolls" n를 수행하기 위해 수정 해야하는 것으로 보입니다 . 그런 식으로 할 필요는 없지만 도움이된다면 거기로 갈 수 있습니다.nn


Jagger / Richards : 항상 원하는 것을 얻을 수있는 것은 아니지만 가끔 시도하면 필요한 것을 얻을 수 있습니다.

목록의 커서

공간 속성을 명확하게 유지하기 위해 snoc 및 cons-list를 사용하여 구조의 구성 요소를 다시 작성하겠습니다. 나는 정의한다

data Bwd x = B0 | Bwd x :< x deriving (Functor, Foldable, Traversable, Show)
data Fwd x = F0 | x :> Fwd x deriving (Functor, Foldable, Traversable, Show)
infixl 5 :<
infixr 5 :>

data Cursor x = Cur (Bwd x) x (Fwd x) deriving (Functor, Foldable, Traversable, Show)

코 모나드를 갖자

class Functor f => Comonad f where
  counit  :: f x -> x
  cojoin  :: f x -> f (f x)

커서가 코 모나드인지 확인하겠습니다.

instance Comonad Cursor where
  counit (Cur _ x _) = x
  cojoin c = Cur (lefts c) c (rights c) where
    lefts (Cur B0 _ _) = B0
    lefts (Cur (xz :< x) y ys) = lefts c :< c where c = Cur xz x (y :> ys)
    rights (Cur _ _ F0) = F0
    rights (Cur xz x (y :> ys)) = c :> rights c where c = Cur (xz :< x) y ys

이런 종류의 물건을 켜면 Cursor공간적으로 즐거운 변형임을 알 수 있습니다.InContext []

InContext f x = (x, ∂f x)

여기서 ∂는 펑터의 형식적 도함수를 취하여 원홀 컨텍스트의 개념을 제공합니다. 이 답변 에서 언급했듯이 InContext f는 항상입니다. 여기서 우리가 가진 것은 차등 구조에 의해 유도 된 것입니다. 여기서 초점에있는 요소를 추출하고 각 요소를 자체 컨텍스트로 장식하여 효과적으로 초점을 다시 맞춘 커서로 가득 찬 컨텍스트를 제공합니다. 초점에 움직이지 않은 커서가 있습니다. 예를 들어 보겠습니다.ComonadComonadcounitcojoin

> cojoin (Cur (B0 :< 1) 2 (3 :> 4 :> F0))
Cur (B0 :< Cur B0 1 (2 :> 3 :> 4 :> F0))
    (Cur (B0 :< 1) 2 (3 :> 4 :> F0))
    (  Cur (B0 :< 1 :< 2) 3 (4 :> F0)
    :> Cur (B0 :< 1 :< 2 :< 3) 4 F0
    :> F0)

보다? 초점이 맞춰진 2는 커서가 2가되도록 장식되었습니다. 왼쪽에는 cursor-at-1의 목록이 있습니다. 오른쪽에는 커서 -at-3 및 커서 -at-4의 목록이 있습니다.

커서 작성, 커서 전치?

자, 당신이 요구하는 구조 ComonadCursor. 가자

newtype (:.:) f g x = C {unC :: f (g x)} deriving Show

코 모나드를 설득 f하고 g작곡 하려면 counit깔끔하게 작곡하지만 '분배 법'이 필요합니다.

transpose :: f (g x) -> g (f x)

그래서 당신은 합성 할 수있다 cojoin이 같은를

f (g x)
  -(fmap cojoin)->
f (g (g x))
  -cojoin->
f (f (g (g x)))
  -(fmap transpose)->
f (g (f (g x)))

어떤 법이 transpose충족 되어야 합니까? 아마도

counit . transpose = fmap counit
cojoin . transpose = fmap transpose . transpose . fmap cojoin

또는 한 순서에서 다른 순서로 어떤 순서의 f와 g를 떼어내는 두 가지 방법이 동일한 결과를 제공하도록 보장하는 데 필요한 모든 것.

transposefor Cursor자체를 정의 할 수 있습니까 ? 전위의 일종을 얻을 수있는 한 가지 방법은 싸게주의하는 것입니다 Bwd하고 Fwd있습니다 zippily 실용적, 따라서 너무입니다 Cursor.

instance Applicative Bwd where
  pure x = pure x :< x
  (fz :< f) <*> (sz :< s) = (fz <*> sz) :< f s
  _ <*> _ = B0

instance Applicative Fwd where
  pure x = x :> pure x
  (f :> fs) <*> (s :> ss) = f s :> (fs <*> ss)
  _ <*> _ = F0

instance Applicative Cursor where
  pure x = Cur (pure x) x (pure x)
  Cur fz f fs <*> Cur sz s ss = Cur (fz <*> sz) (f s) (fs <*> ss)

그리고 여기서 쥐 냄새를 맡기 시작해야합니다. 모양이 일치하지 않으면 잘림이 발생하며 , 이는 자체 조옮김이 자체 역전이라는 명백히 바람직한 속성을 깨뜨릴 것입니다. 어떤 종류의 비정형도 살아남지 못할 것입니다. 우리는 전치 연산자를 얻습니다 : sequenceA, 그리고 완전히 정규적인 데이터의 경우 모든 것이 밝고 아름답습니다.

> regularMatrixCursor
Cur (B0 :< Cur (B0 :< 1) 2 (3 :> F0))
          (Cur (B0 :< 4) 5 (6 :> F0))
          (Cur (B0 :< 7) 8 (9 :> F0) :> F0)
> sequenceA regularMatrixCursor
Cur (B0 :< Cur (B0 :< 1) 4 (7 :> F0))
          (Cur (B0 :< 2) 5 (8 :> F0))
          (Cur (B0 :< 3) 6 (9 :> F0) :> F0)

그러나 내부 커서 중 하나를 정렬에서 벗어나더라도 (크기를 엉망으로 만들지 마십시오) 일이 잘못됩니다.

> raggedyMatrixCursor
Cur (B0 :< Cur ((B0 :< 1) :< 2) 3 F0)
          (Cur (B0 :< 4) 5 (6 :> F0))
          (Cur (B0 :< 7) 8 (9 :> F0) :> F0)
> sequenceA raggedyMatrixCursor
Cur (B0 :< Cur (B0 :< 2) 4 (7 :> F0))
          (Cur (B0 :< 3) 5 (8 :> F0))
          F0

하나의 외부 커서 위치와 여러 개의 내부 커서 위치가 있으면 잘 작동하는 조옮김이 없습니다. 자체 구성을 Cursor사용하면 내부 구조가 서로 상대적으로 비정형이 될 수 있으므로 아니요 transpose, 아니요 cojoin. 당신은 정의 할 수 있고

instance (Comonad f, Traversable f, Comonad g, Applicative g) =>
  Comonad (f :.: g) where
    counit = counit . counit . unC
    cojoin = C . fmap (fmap C . sequenceA) . cojoin . fmap cojoin . unC

그러나 내부 구조를 규칙적으로 유지하는 것은 우리에게 책임이 있습니다. 당신이 그 부담을 받아 들일 경우 때문에, 당신은, 반복 할 수 ApplicativeTraversable쉽게 구성에서 닫힙니다. 다음은 비트와 조각입니다.

instance (Functor f, Functor g) => Functor (f :.: g) where
  fmap h (C fgx) = C (fmap (fmap h) fgx)

instance (Applicative f, Applicative g) => Applicative (f :.: g) where
  pure = C . pure . pure
  C f <*> C s = C (pure (<*>) <*> f <*> s)

instance (Functor f, Foldable f, Foldable g) => Foldable (f :.: g) where
  fold = fold . fmap fold . unC

instance (Traversable f, Traversable g) => Traversable (f :.: g) where
  traverse h (C fgx) = C <$> traverse (traverse h) fgx

편집 : 완전성을 위해 모든 것이 규칙적 일 때 수행하는 작업은 다음과 같습니다.

> cojoin (C regularMatrixCursor)
C {unC = Cur (B0 :< Cur (B0 :<
  C {unC = Cur B0 (Cur B0 1 (2 :> (3 :> F0))) (Cur B0 4 (5 :> (6 :> F0)) :> (Cur B0 7 (8 :> (9 :> F0)) :> F0))}) 
 (C {unC = Cur B0 (Cur (B0 :< 1) 2 (3 :> F0)) (Cur (B0 :< 4) 5 (6 :> F0) :> (Cur (B0 :< 7) 8 (9 :> F0) :> F0))})
 (C {unC = Cur B0 (Cur ((B0 :< 1) :< 2) 3 F0) (Cur ((B0 :< 4) :< 5) 6 F0 :> (Cur ((B0 :< 7) :< 8) 9 F0 :> F0))} :> F0))
(Cur (B0 :<
  C {unC = Cur (B0 :< Cur B0 1 (2 :> (3 :> F0))) (Cur B0 4 (5 :> (6 :> F0))) (Cur B0 7 (8 :> (9 :> F0)) :> F0)})
 (C {unC = Cur (B0 :< Cur (B0 :< 1) 2 (3 :> F0)) (Cur (B0 :< 4) 5 (6 :> F0)) (Cur (B0 :< 7) 8 (9 :> F0) :> F0)}) 
 (C {unC = Cur (B0 :< Cur ((B0 :< 1) :< 2) 3 F0) (Cur ((B0 :< 4) :< 5) 6 F0) (Cur ((B0 :< 7) :< 8) 9 F0 :> F0)} :> F0))
(Cur (B0 :<
  C {unC = Cur ((B0 :< Cur B0 1 (2 :> (3 :> F0))) :< Cur B0 4 (5 :> (6 :> F0))) (Cur B0 7 (8 :> (9 :> F0))) F0})
 (C {unC = Cur ((B0 :< Cur (B0 :< 1) 2 (3 :> F0)) :< Cur (B0 :< 4) 5 (6 :> F0)) (Cur (B0 :< 7) 8 (9 :> F0)) F0})
 (C {unC = Cur ((B0 :< Cur ((B0 :< 1) :< 2) 3 F0) :< Cur ((B0 :< 4) :< 5) 6 F0) (Cur ((B0 :< 7) :< 8) 9 F0) F0} :> F0)
:> F0)}

Hancock의 Tensor 제품

규칙 성을 위해서는 구성보다 더 강한 것이 필요합니다. 당신은 "g-structures-all-the-same-shape의 f- 구조"의 개념을 포착 할 수 있어야합니다. 이것은 추정 할 수없는 Peter Hancock이 "tensor product"라고 부르는 것입니다. 제가 쓸 것입니다 f :><: g. 하나의 "외부"f- 모양과 모든 내부 g- 구조에 공통 인 하나의 "내부"g- 모양이 있으므로 전치가 쉽게 정의 될 수 있습니다. 항상 자기 반대입니다. Hancock의 텐서는 Haskell에서 편리하게 정의 할 수는 없지만 종속 유형 설정에서는이 텐서를 갖는 "컨테이너"개념을 쉽게 공식화 할 수 있습니다.

아이디어를 제공하기 위해 컨테이너의 퇴보 개념을 고려하십시오.

data (:<|) s p x = s :<| (p -> x)

우리가 말하는 곳 s은 "모양" p의 유형과 "위치"의 유형입니다. 값은 모양의 선택과 x각 위치 의 저장으로 구성됩니다 . 종속적 인 경우 위치 유형은 모양의 선택에 따라 달라질 수 있습니다 (예 : 목록의 경우 모양은 숫자 (길이)이고 그만큼 많은 위치가 있음). 이 컨테이너에는 텐서 제품이 있습니다.

(s :<| p) :><: (s' :<| p')  =  (s, s') :<| (p, p')

이것은 일반화 된 행렬과 같습니다. 한 쌍의 모양이 차원을 제공하고 각 위치 쌍에 요소가 있습니다. p및의 p'값을 입력 하고 의존 할 때이 작업을 완벽하게 수행 할 수 s있으며 s', 이는 컨테이너의 텐서 곱에 대한 Hancock의 정의입니다.

Tensor 제품에 대한 InContext

고등학교에서 배운 수 있으므로 지금, ∂(s :<| p) = (s, p) :<| (p-1)어디에 p-1이상 적은 수의 요소를 몇 가지 유형입니다 p. ∂ (s x ^ p) = (s p) * x ^ (p-1)과 같습니다. 한 위치를 선택 (모양에 기록)하고 삭제합니다. 걸림돌은 p-1종속 유형없이 손을 잡는 것이 까다 롭다 는 것 입니다. 그러나 삭제하지 않고InContext 위치 선택 합니다 .

InContext (s :<| p) ~= (s, p) :<| p

이것은 종속 사례에서도 잘 작동하며 우리는

InContext (f :><: g) ~= InContext f :><: InContext g

이제 우리는 그것이 InContext f항상 a 라는 것을 알고 있습니다. Comonad이것은 InContexts의 텐서 곱이 그 자체가 InContexts 이기 때문에 코모 나딕이라는 것을 알려줍니다 . 즉, 차원 당 하나의 위치를 ​​선택합니다 (전체적으로 정확히 하나의 위치를 ​​제공함). 이전에는 하나의 외부 위치와 많은 내부 위치가있었습니다. 구성을 대체하는 텐서 제품으로 모든 것이 멋지게 작동합니다.

나 페리 안 펑터

그러나 Functor텐서 곱과 구성이 일치 하는 하위 클래스가 있습니다. 이들은하다 Functorf하는 f () ~ ()그래서 조성물에 누더기 값은 처음부터 배제되어 즉, 어쨌든 하나의 형태가있다 :. Functor들 모두 동형있는 (p ->)몇 가지 위치 세트에 대해 p우리가로 생각할 수 대수 (지수되는 x줄 제기해야한다 f x). 이에 따라 Hancock은 이러한 Naperian펑터를 John Napier (Hancock이 사는 에딘버러 지역에 유령이있는 유령)의 이름을 따서 부릅니다 .

class Applicative f => Naperian f where
  type Log f
  project :: f x -> Log f -> x
  positions :: f (Log f)
  --- project positions = id

Naperian펑은 유도 대수를 가지고 project있다 찾아낸 요소 이온 함수 매핑 위치. Naperian펑 모든 zippily이다 Applicative으로 pure그리고 <*>돌출부에 대한 K 및 S 연결자에 대응. 또한 각 위치에서 해당 위치의 표현이 저장되는 값을 구성 할 수도 있습니다. 당신이 기억할 수있는 로그의 법칙이 즐겁게 나타납니다.

newtype Id x = Id {unId :: x} deriving Show

instance Naperian Id where
  type Log Id = ()
  project (Id x) () = x
  positions = Id ()

newtype (:*:) f g x = Pr (f x, g x) deriving Show

instance (Naperian f, Naperian g) => Naperian (f :*: g) where
  type Log (f :*: g) = Either (Log f) (Log g)
  project (Pr (fx, gx)) (Left p) = project fx p
  project (Pr (fx, gx)) (Right p) = project gx p
  positions = Pr (fmap Left positions, fmap Right positions)

고정 크기 배열 ( 벡터 ) 은로 주어집니다 (Id :*: Id :*: ... :*: Id :*: One). 여기서는 One로그가이고 상수 단위 펑터입니다 Void. 그래서 배열은 Naperian. 이제 우리는 또한

instance (Naperian f, Naperian g) => Naperian (f :.: g) where
  type Log (f :.: g) = (Log f, Log g)
  project (C fgx) (p, q) = project (project fgx p) q
  positions = C $ fmap (\ p -> fmap (p ,) positions) positions

이는 다차원 배열이 Naperian.

InContext ffor 의 버전을 구성하려면 Naperian f위치를 가리 킵니다!

data Focused f x = f x :@ Log f

instance Functor f => Functor (Focused f) where
  fmap h (fx :@ p) = fmap h fx :@ p

instance Naperian f => Comonad (Focused f) where
  counit (fx :@ p) = project fx p
  cojoin (fx :@ p) = fmap (fx :@) positions :@ p

따라서 특히 Focusedn 차원 배열은 실제로 코 모나드가 될 것입니다. 벡터의 구성은 벡터가이므로 n 벡터의 텐서 곱입니다 Naperian. 그러나 Focusedn 차원 배열은 차원 을 결정 하는 n 개의 벡터 의 구성이 아닌 n- 겹 텐서 곱이 Focused됩니다. 이 코 모나드를 지퍼로 표현하려면 텐서 곱을 구성 할 수있는 형태로 표현해야합니다. 나는 그것을 미래를위한 연습으로 남겨 둘 것이다.


pigworkers 게시물 및 http://hackage.haskell.org/packages/archive/representable-functors/3.0.0.1/doc/html/Data-Functor-Representable.html 에서 영감을 얻은 한 번 더 시도 하십시오 .

대표 가능 (또는 나 페리 안) 펑 터는 키 (또는 로그)가 모노 이드 인 경우 코 모나드 자체입니다! 그런 다음 coreturn위치에서 값을 가져옵니다 mempty. 그리고 cojoin mappend그것이 사용 가능한 두 개의 키입니다. (에 대한 comonad 인스턴스와 같습니다 (p ->).)

{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

import Data.List (genericIndex)
import Data.Monoid
import Data.Key
import Data.Functor.Representable

data Nat = Z | S Nat

data U (n :: Nat) x where
  Point :: x -> U Z x
  Dimension :: [U n x] -> U n x -> [U n x] -> U (S n) x

dmap :: (U n x -> U m r) -> U (S n) x -> U (S m) r
dmap f (Dimension ls mid rs) = Dimension (map f ls) (f mid) (map f rs)

instance Functor (U n) where
  fmap f (Point x) = Point (f x)
  fmap f d@Dimension{} = dmap (fmap f) d

class Functor w => Comonad w where
  (=>>)    :: w a -> (w a -> b) -> w b
  coreturn :: w a -> a
  cojoin   :: w a -> w (w a)

  x =>> f = fmap f (cojoin x)
  cojoin xx = xx =>> id

U목록이 무한히 길면 표현 가능합니다. 그러면 모양이 하나뿐입니다. 의 키는 U nn 개의 정수로 구성된 벡터입니다.

type instance Key (U n) = UKey n

data UKey (n :: Nat) where
  P :: UKey Z
  D :: Integer -> UKey n -> UKey (S n)

instance Lookup (U n) where lookup = lookupDefault
instance Indexable (U n) where
  index (Point x) P = x
  index (Dimension ls mid rs) (D i k) 
    | i < 0 = index (ls `genericIndex` (-i - 1)) k
    | i > 0 = index (rs `genericIndex` ( i - 1)) k
    | otherwise = index mid k

우리는 분할해야 Representable이가지 경우, 하나의 인스턴스를 Z하고 하나 S우리는 유형의 값이 없기 때문에, U n에 패턴 일치에있다.

instance Representable (U Z) where
  tabulate f = Point (f P)
instance Representable (U n) => Representable (U (S n)) where
  tabulate f = Dimension 
    (map (\i -> tabulate (f . D (-i))) [1..]) 
    (tabulate (f . D 0))
    (map (\i -> tabulate (f . D   i)) [1..])

instance Monoid (UKey Z) where
  mempty = P
  mappend P P = P
instance Monoid (UKey n) => Monoid (UKey (S n)) where
  mempty = D 0 mempty
  mappend (D il kl) (D ir kr) = D (il + ir) (mappend kl kr)

그리고의 핵심 U n은 실제로 monoid이므로 U nrepresentable-functor 패키지의 기본 구현을 사용하여 comonad로 전환 할 수 있습니다 .

instance (Monoid (UKey n), Representable (U n)) => Comonad (U n) where
  coreturn = extractRep
  cojoin = duplicateRep
  (=>>) = flip extendRep

이번에는 몇 가지 테스트를했습니다.

testVal :: U (S (S Z)) Int
testVal = Dimension 
  (repeat (Dimension (repeat (Point 1)) (Point 2) (repeat (Point 3))))
          (Dimension (repeat (Point 4)) (Point 5) (repeat (Point 6)))
  (repeat (Dimension (repeat (Point 7)) (Point 8) (repeat (Point 9))))

-- Hacky Eq instance, just for testing
instance Eq x => Eq (U n x) where
  Point a == Point b = a == b
  Dimension la a ra == Dimension lb b rb = take 3 la == take 3 lb && a == b && take 3 ra == take 3 rb

instance Show x => Show (U n x) where
  show (Point x) = "(Point " ++ show x ++ ")"
  show (Dimension l a r) = "(Dimension " ++ show (take 2 l) ++ " " ++ show a ++ " " ++ show (take 2 r) ++ ")"

test = 
  coreturn (cojoin testVal) == testVal && 
  fmap coreturn (cojoin testVal) == testVal && 
  cojoin (cojoin testVal) == fmap cojoin (cojoin testVal)

So this turns out to be wrong. I'll leave it here in case anybody wants to try to fix it.

This implementation is the way @pigworker suggested I think. It compiles, but I haven't tested it. (I took the cojoin1 implementation from http://blog.sigfpe.com/2006/12/evaluating-cellular-automata-is.html)

{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}

data Nat = Z | S Nat

data U (n :: Nat) x where
  Point :: x -> U Z x
  Dimension :: [U n x] -> U n x -> [U n x] -> U (S n) x

unPoint :: U Z x -> x
unPoint (Point x) = x

dmap :: (U n x -> U m r) -> U (S n) x -> U (S m) r
dmap f (Dimension ls mid rs) = Dimension (map f ls) (f mid) (map f rs)

right, left :: U (S n) x -> U (S n) x
right (Dimension a b (c:cs)) = Dimension (b:a) c cs
left  (Dimension (a:as) b c) = Dimension as a (b:c)

instance Functor (U n) where
  fmap f (Point x) = Point (f x)
  fmap f d@Dimension{} = dmap (fmap f) d

class Functor w => Comonad w where
  (=>>)    :: w a -> (w a -> b) -> w b
  coreturn :: w a -> a
  cojoin   :: w a -> w (w a)

  x =>> f = fmap f (cojoin x)
  cojoin xx = xx =>> id

instance Comonad (U n) where
  coreturn (Point x) = x
  coreturn (Dimension _ mid _) = coreturn mid
  cojoin (Point x) = Point (Point x)
  cojoin d@Dimension{} = fmap unlayer . unlayer . fmap dist . cojoin1 . fmap cojoin . layer $ d

dist :: U (S Z) (U n x) -> U n (U (S Z) x)
dist = layerUnder . unlayer

layerUnder :: U (S n) x -> U n (U (S Z) x)
layerUnder d@(Dimension _ Point{} _) = Point d
layerUnder d@(Dimension _ Dimension{} _) = dmap layerUnder d

unlayer :: U (S Z) (U n x) -> U (S n) x
unlayer = dmap unPoint

layer :: U (S n) x -> U (S Z) (U n x)
layer = dmap Point

cojoin1 :: U (S Z) x -> U (S Z) (U (S Z) x)
cojoin1 a = layer $ Dimension (tail $ iterate left a) a (tail $ iterate right a)

ReferenceURL : https://stackoverflow.com/questions/12963733/writing-cojoin-or-cobind-for-n-dimensional-grid-type

반응형