Предположим, я определяю свой собственный тип списка.
data MyVec : Nat -> Type -> Type where
MyNil : MyVec Z a
(::) : a -> MyVec k a -> MyVec (S k) a
И функция myMap
, выполняющая роль fmap
для MyVec
:
myMap : (a -> b) -> MyVec k a -> MyVec k b
myMap f MyNil = ?rhs_nil
myMap f (x :: xs) = ?rhs_cons
Попытка решить ?rhs_nil
дыру в моем сознании:
:t ?rhs_nil
isMyVec 0 b
- достаточно справедливо - мне нужен конструктор, который возвращает
MyVec
, параметризованныйb
, и мне нужно, чтобыk
было0
(илиZ
), и я вижу, чтоMyNil
индексируетсяZ
и параметризуется чем угодно, поэтому я могу легко использоватьb
, поэтому?rhs_nil
=MyNil
и он проверяет тип, денди :t ?rhs_cons
isMyVec (S k)
- Мне нужен конструктор, который возвращает
MyVec
с параметрамиb
, и мне нужно, чтобыk
было(S k)
Я вижу, что конструктор (::)
создает список, индексированный (S k)
, и я пытаюсь его использовать. Первый аргумент должен быть типа b
, учитывая, что я создаю MyVec <> b
, и единственный способ получить его — применить x
к f
.
myMap f (x :: xs) = f x :: <>
Теперь я путаюсь. RHS (::)
должен быть MyVec k b
, почему я не могу просто использовать конструктор MyNil
с объединением/заменой k == Z
(который MyNil
) дает мне, получая:
myMap f (x :: xs) = f x :: MyNil
Я понимаю, что мне нужно рекурсивно и иметь = f x :: myMap f xs
, но как компилятор узнает, сколько раз нужно применить конструктор (::)
? Как он выводит правильный k
для этого случая, не позволяя мне использовать там Z
.