Відповіді:
Я не відповім на вашу вправу для вас - для вправ краще зрозуміти відповідь самостійно - але ось підказка, яка повинна привести вас до відповіді: ви можете визначити список з 0 до 2 елементів як
data List a = None | One a | Two a a
А тепер подумайте, як можна розширити це до п’яти елементів.
Ну, рекурсивне рішення - це звичайно нормальна і насправді приємна річ у Haskell, але трохи складніше обмежувати кількість елементів тоді. Отже, для простого вирішення проблеми спочатку розгляньте дурний, але працездатний, наданий bradm.
У випадку рекурсивного рішення фокус полягає в тому, щоб пропустити змінну «лічильник» вниз по рекурсії, а потім відключити витрату більше елементів, коли ви досягнете максимально дозволеного. Це можна добре зробити за допомогою GADT:
{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeInType, StandaloneDeriving #-}
import Data.Kind
import GHC.TypeLits
infixr 5 :#
data ListMax :: Nat -> Type -> Type where
Nil :: ListMax n a
(:#) :: a -> ListMax n a -> ListMax (n+1) a
deriving instance (Show a) => Show (ListMax n a)
Тоді
*Main> 0:#1:#2:#Nil :: ListMax 5 Int
0 :# (1 :# (2 :# Nil))
*Main> 0:#1:#2:#3:#4:#5:#6:#Nil :: ListMax 5 Int
<interactive>:13:16: error:
• Couldn't match type ‘1’ with ‘0’
Expected type: ListMax 0 Int
Actual type: ListMax (0 + 1) Int
• In the second argument of ‘(:#)’, namely ‘5 :# 6 :# Nil’
In the second argument of ‘(:#)’, namely ‘4 :# 5 :# 6 :# Nil’
In the second argument of ‘(:#)’, namely ‘3 :# 4 :# 5 :# 6 :# Nil’
Для повноти дозвольте додати «потворний» альтернативний підхід, який, проте, є досить базовим.
Нагадаємо, Maybe a
це тип, значення якого мають форму Nothing
або Just x
для деяких x :: a
.
Отже, переосмислюючи наведені вище значення, ми можемо розглядати Maybe a
як "тип списку з обмеженим доступом", де списки можуть мати нуль або один елемент.
Тепер (a, Maybe a)
просто додаємо ще один елемент, тож це "тип списку", де списки можуть мати один ( (x1, Nothing)
) або два ( (x1, Just x2)
) елементи.
Отже, Maybe (a, Maybe a)
це "тип списку", де списки можуть мати нуль ( Nothing
), один ( Just (x1, Nothing)
) або два ( (Just (x1, Just x2)
) елементи.
Тепер ви повинні мати можливість зрозуміти, як діяти. Дозвольте ще раз наголосити, що це не зручне рішення для використання, але це (ІМО) приємна вправа зрозуміти це все одно.
Використовуючи деякі розширені функції Haskell, ми можемо узагальнити вищезазначене, використовуючи сімейство типів:
type family List (n :: Nat) (a :: Type) :: Type where
List 0 a = ()
List n a = Maybe (a, List (n-1) a)
a
з в Either () (a, Either () (a, Either () (a, Either () ())))
... цікавому типі алгебри foldr (.) id (replicate 3 $ ([0] ++) . liftA2 (+) [1]) $ [0] == [0,1,2,3]
.