# 构造我们自己的 Types 和 Typeclasses

## Algebraic Data Types 入门

data Bool = False | True

data 表示我们要定义一个新的型别。= 的左端标明型别的名称即 Bool= 的右端就是值构造子 (Value Constructor)，它们明确了该型别可能的值。| 读作"或"，所以可以这样阅读该声明：Bool 型别的值可以是 TrueFalse。型别名和值构造子的首字母必大写。

data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647

data Shape = Circle Float Float Float | Rectangle Float Float Float Float

ghci> :t Circle
Circle :: Float -> Float -> Float -> Shape
ghci> :t Rectangle
Rectangle :: Float -> Float -> Float -> Float -> Shape

Cool，这么说值构造子就跟普通函数并无二致啰，谁想得到？我们写个函数计算图形面积：

surface :: Shape -> Float
surface (Circle _ _ r) = pi * r ^ 2
surface (Rectangle x1 y1 x2 y2) = (abs $x2 - x1) * (abs$ y2 - y1)

ghci> surface $Circle 10 20 10 314.15927 ghci> surface$ Rectangle 0 0 100 100
10000.0

Yay，it works！不过我们若尝试输出 Circle 10 20 到控制台，就会得到一个错误。这是因为 Haskell 还不知道该型别的字元串表示方法。想想，当我们往控制台输出值的时候，Haskell 会先呼叫 show 函数得到这个值的字元串表示才会输出。因此要让我们的 Shape 型别成为 Show 型别类的成员。可以这样修改：

data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)

ghci> Circle 10 20 5
Circle 10.0 20.0 5.0
ghci> Rectangle 50 230 60 90
Rectangle 50.0 230.0 60.0 90.0

ghci> map (Circle 10 20) [4,5,6,6]
[Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]

data Point = Point Float Float deriving (Show)
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)

surface :: Shape -> Float
surface (Circle _ r) = pi * r ^ 2
surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $x2 - x1) * (abs$ y2 - y1)

ghci> surface (Rectangle (Point 0 0) (Point 100 100))
10000.0
ghci> surface (Circle (Point 0 0) 24)
1809.5574

nudge :: Shape -> Float -> Float -> Shape
nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))

ghci> nudge (Circle (Point 34 34) 10) 5 10
Circle (Point 39.0 44.0) 10.0

baseCircle :: Float -> Shape
baseCircle r = Circle (Point 0 0) r

baseRect :: Float -> Float -> Shape
baseRect width height = Rectangle (Point 0 0) (Point width height)
ghci> nudge (baseRect 40 100) 60 23
Rectangle (Point 60.0 23.0) (Point 100.0 123.0)

module Shapes
( Point(..)
, Shape(..)
, surface
, nudge
, baseCircle
, baseRect
) where

## Record Syntax

OK，我们需要一个数据型别来描述一个人，得包含他的姓、名、年龄、身高、体重、电话号码以及最爱的冰激淋。我不知你的想法，不过我觉得要了解一个人，这些资料就够了。就这样，实现出来！

data Person = Person String String Int Float String String deriving (Show)

O~Kay，第一项是名，第二项是姓，第三项是年龄，等等。我们造一个人：

ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
ghci> guy
Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"

firstName :: Person -> String
firstName (Person firstname _ _ _ _ _) = firstname

lastName :: Person -> String
lastName (Person _ lastname _ _ _ _) = lastname

age :: Person -> Int
age (Person _ _ age _ _ _) = age

height :: Person -> Float
height (Person _ _ _ height _ _) = height

phoneNumber :: Person -> String
phoneNumber (Person _ _ _ _ number _) = number

flavor :: Person -> String
flavor (Person _ _ _ _ _ flavor) = flavor

ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
ghci> firstName guy
"Buddy"
ghci> height guy
184.2
ghci> flavor guy
"Chocolate"

data Person = Person { firstName :: String
, lastName :: String
, age :: Int
, height :: Float
, phoneNumber :: String
, flavor :: String
} deriving (Show)

ghci> :t flavor
flavor :: Person -> String
ghci> :t firstName
firstName :: Person -> String

data Car = Car String String Int deriving (Show)
ghci> Car "Ford" "Mustang" 1967
Car "Ford" "Mustang" 1967

data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)
ghci> Car {company="Ford", model="Mustang", year=1967}
Car {company = "Ford", model = "Mustang", year = 1967}

## Type parameters

data Maybe a = Nothing | Just a

ghci> Just "Haha"
Just "Haha"
ghci> Just 84
Just 84
ghci> :t Just "Haha"
Just "Haha" :: Maybe [Char]
ghci> :t Just 84
Just 84 :: (Num t) => Maybe t
ghci> :t Nothing
Nothing :: Maybe a
ghci> Just 10 :: Maybe Double
Just 10.0

data Car = Car { company :: String
, model :: String
, year :: Int
} deriving (Show)

data Car a b c = Car { company :: a
, model :: b
, year :: c
} deriving (Show)

tellCar :: Car -> String
tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
ghci> let stang = Car {company="Ford", model="Mustang", year=1967}
ghci> tellCar stang
"This Ford Mustang was made in 1967"

tellCar :: (Show a) => Car String String a -> String
tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y

ghci> tellCar (Car "Ford" "Mustang" 1967)
"This Ford Mustang was made in 1967"
ghci> tellCar (Car "Ford" "Mustang" "nineteen sixty seven")
"This Ford Mustang was made in \"nineteen sixty seven\""
ghci> :t Car "Ford" "Mustang" 1967
Car "Ford" "Mustang" 1967 :: (Num t) => Car [Char] [Char] t
ghci> :t Car "Ford" "Mustang" "nineteen sixty seven"
Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char]

data (Ord k) => Map k v = ...

data Vector a = Vector a a a deriving (Show)
vplus :: (Num t) => Vector t -> Vector t -> Vector t
(Vector i j k) vplus (Vector l m n) = Vector (i+l) (j+m) (k+n)
vectMult :: (Num t) => Vector t -> t -> Vector t
(Vector i j k) vectMult m = Vector (i*m) (j*m) (k*m)
scalarMult :: (Num t) => Vector t -> Vector t -> t
(Vector i j k) scalarMult (Vector l m n) = i*l + j*m + k*n

vplus 用来相加两个向量，即将其所有对应的项相加。scalarMult 用来求两个向量的标量积，vectMult 求一个向量和一个标量的积。这些函数可以处理 Vector IntVector IntegerVector Float 等等型别，只要 Vector a 里的这个 aNum 型别类中就行。同样，如果你看下这些函数的型别声明就会发现，它们只能处理相同型别的向量，其中包含的数字型别必须与另一个向量一致。注意，我们并没有在 data 声明中添加 Num 的类约束。反正无论怎么着都是给函数加约束。

ghci> Vector 3 5 8 vplus Vector 9 2 8
Vector 12 7 16
ghci> Vector 3 5 8 vplus Vector 9 2 8 vplus Vector 0 2 3
Vector 12 9 19
ghci> Vector 3 9 7 vectMult 10
Vector 30 90 70
ghci> Vector 4 9 5 scalarMult Vector 9.0 2.0 4.0
74.0
ghci> Vector 2 9 3 vectMult (Vector 4 9 5 scalarMult Vector 9 2 4)
Vector 148 666 222

## Derived instances

data Person = Person { firstName :: String
, lastName :: String
, age :: Int
}

data Person = Person { firstName :: String
, lastName :: String
, age :: Int
} deriving (Eq)

ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
ghci> let adRock = Person {firstName = "Adam", lastName = "Horovitz", age = 41}
ghci> let mca = Person {firstName = "Adam", lastName = "Yauch", age = 44}
False
False
ghci> mikeD == mikeD
True
ghci> mikeD == Person {firstName = "Michael", lastName = "Diamond", age = 43}
True

ghci> let beastieBoys = [mca, adRock, mikeD]
ghci> mikeD elem beastieBoys
True

ShowRead 型别类处理可与字元串相互转换的东西。同 Eq 相似，如果一个型别的构造子含有参数，那所有参数的型别必须都得属于 ShowRead 才能让该型别成为其 instance。就让我们的 Person 也成为 ReadShow 的一员吧。

data Person = Person { firstName :: String
, lastName :: String
, age :: Int
} deriving (Eq, Show, Read)

ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
ghci> mikeD
Person {firstName = "Michael", lastName = "Diamond", age = 43}
ghci> "mikeD is: " ++ show mikeD
"mikeD is: Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}"

Read 几乎就是与 Show 相对的型别类，show 是将一个值转换成字元串，而 read 则是将一个字元串转成某型别的值。还记得，使用 read 函数时我们必须得用型别注释注明想要的型别，否则 Haskell 就不会知道如何转换。

ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" :: Person
Person {firstName = "Michael", lastName = "Diamond", age = 43}

ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" == mikeD
True

data Bool = False | True deriving (Ord)

ghci> True compare False
GT
ghci> True > False
True
ghci> True < False
False

Maybe a 数据型别中，值构造子 NothingJust 值构造子前面，所以一个 Nothing 总要比 Just something 的值小。即便这个 something-100000000 也是如此。

ghci> Nothing < Just 100
True
ghci> Nothing > Just (-49999)
False
ghci> Just 3 compare Just 2
GT
ghci> Just 100 > Just 50
True

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving (Eq, Ord, Show, Read, Bounded, Enum)

ghci> Wednesday
Wednesday
ghci> show Wednesday
"Wednesday"
Saturday

ghci> Saturday == Sunday
False
ghci> Saturday == Saturday
True
ghci> Saturday > Friday
True
ghci> Monday compare Wednesday
LT

ghci> minBound :: Day
Monday
ghci> maxBound :: Day
Sunday

ghci> succ Monday
Tuesday
ghci> pred Saturday
Friday
ghci> [Thursday .. Sunday]
[Thursday,Friday,Saturday,Sunday]
ghci> [minBound .. maxBound] :: [Day]
[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]

## Type synonyms

type String = [Char]

phoneBook :: [(String,String)]
phoneBook =
[("betty","555-2938")
,("bonnie","452-2928")
,("patsy","493-2928")
,("lucille","205-2928")
,("wendy","939-8282")
,("penny","853-2492")
]

type PhoneBook = [(String,String)]

type PhoneNumber = String
type Name = String
type PhoneBook = [(Name,PhoneNumber)]

inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool
inPhoneBook name pnumber pbook = (name,pnumber) elem pbook

type AssocList k v = [(k,v)]

*Fronzie 说*：Hey！当我提到具体型别，那我就是说它是完全呼叫的，就像 Map Int String。要不就是多态函数中的 [a] 或 (Ord a) => Maybe a 之类。有时我和孩子们会说 "Maybe 型别"，但我们的意思并不是按字面来，傻瓜都知道 Maybe 是型别构造子嘛。只要用一个明确的型别呼叫 Maybe，如 Maybe String 可得一个具体型别。你知道，只有具体型别才可以储存值。

type IntMap v = Map Int v

type IntMap = Map Int

Oh yeah，如果要你去实现它，很可能会用个 qualified import 来导入 Data.Map。这时，型别构造子前面必须得加上模组名。所以应该写个 type IntMap = Map.Map Int

data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)

ghci> Right 20
Right 20
ghci> Left "w00t"
Left "w00t"
ghci> :t Right 'a'
Right 'a' :: Either a Char
ghci> :t Left True
Left True :: Either Bool b

import qualified Data.Map as Map

data LockerState = Taken | Free deriving (Show, Eq)

type Code = String

type LockerMap = Map.Map Int (LockerState, Code)

lockerLookup :: Int -> LockerMap -> Either String Code
lockerLookup lockerNumber map =
case Map.lookup lockerNumber map of
Nothing -> Left $"Locker number " ++ show lockerNumber ++ " doesn't exist!" Just (state, code) -> if state /= Taken then Right code else Left$ "Locker " ++ show lockerNumber ++ " is already taken!"

lockers :: LockerMap
lockers = Map.fromList
[(100,(Taken,"ZD39I"))
,(101,(Free,"JAH3I"))
,(103,(Free,"IQSA9"))
,(105,(Free,"QOTSA"))
,(109,(Taken,"893JJ"))
,(110,(Taken,"99292"))
]

ghci> lockerLookup 101 lockers
Right "JAH3I"
ghci> lockerLookup 100 lockers
Left "Locker 100 is already taken!"
ghci> lockerLookup 102 lockers
Left "Locker number 102 doesn't exist!"
ghci> lockerLookup 110 lockers
Left "Locker 110 is already taken!"
ghci> lockerLookup 105 lockers
Right "QOTSA"

## Recursive data structures (递回地定义资料结构)

data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)

data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)

ghci> Empty
Empty
ghci> 5 Cons Empty
Cons 5 Empty
ghci> 4 Cons (5 Cons Empty)
Cons 4 (Cons 5 Empty)
ghci> 3 Cons (4 Cons (5 Cons Empty))
Cons 3 (Cons 4 (Cons 5 Empty))

infixr 5 :-:
data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)

ghci> 3 :-: 4 :-: 5 :-: Empty
(:-:) 3 ((:-:) 4 ((:-:) 5 Empty))
ghci> let a = 3 :-: 4 :-: 5 :-: Empty
ghci> 100 :-: a
(:-:) 100 ((:-:) 3 ((:-:) 4 ((:-:) 5 Empty)))

Haskell 在宣告 deriving Show 的时候，他会仍视构造子为前缀函数，因此必须要用括号括起来。

infixr 5  ++
(++) :: [a] -> [a] -> [a]
[]     ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)

infixr 5  .++
(.++) :: List a -> List a -> List a
Empty .++ ys = ys
(x :-: xs) .++ ys = x :-: (xs .++ ys)

ghci> let a = 3 :-: 4 :-: 5 :-: Empty
ghci> let b = 6 :-: 7 :-: Empty
ghci> a .++ b
(:-:) 3 ((:-:) 4 ((:-:) 5 ((:-:) 6 ((:-:) 7 Empty))))

Data.SetData.Map 中的 set 和 Map 都是用树来实现的，只是他们是用平衡二元搜寻树而不是随意的二元搜寻树。不过这边我们就只先写一棵普通的二元搜寻树就好了。

data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)

singleton :: a -> Tree a
singleton x = Node x EmptyTree EmptyTree

treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = singleton x
treeInsert x (Node a left right)
| x == a = Node x left right
| x < a  = Node a (treeInsert x left) right
| x > a  = Node a left (treeInsert x right)

singleton 函数只是一个做一个含有两棵空子树的节点的函数的别名。在插入的操作中，我们先为终端条件定义了一个模式匹配。如果我们走到了一棵空的子树，这表示我们到达了我们想要的地方，我们便建造一棵空的单元素的树来放在那个位置。如果我们还没走到一棵空的树来插入我们的元素。那就必须要做一些检查来往下走。如果我们要安插的元素跟 root 所含有的元素相等，那就直接回传这棵树。如果安插的元素比较小，就回传一棵新的树。这棵树的 root 跟原来的相同，右子树也相同，只差在我们要安插新的元素到左子树中。如果安插的元素反而比较大，那整个过程就相反。

treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False
treeElem x (Node a left right)
| x == a = True
| x < a  = treeElem x left
| x > a  = treeElem x right

ghci> let nums = [8,6,4,1,7,3,5]
ghci> let numsTree = foldr treeInsert EmptyTree nums
ghci> numsTree
Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))

foldr 中，treeInsert 是做 folding 操作的函数，而 EmptyTree 是起始的 accumulator，nums 则是要被走遍的 List。

ghci> 8 treeElem numsTree
True
ghci> 100 treeElem numsTree
False
ghci> 1 treeElem numsTree
True
ghci> 10 treeElem numsTree
False

## Typeclasses 的第二堂课

Typeclass 跟 Java 或 Python 中的 class 一点关系也没有。这个概念让很多人混淆，所以我希望你先忘掉所有在命令式语言中学到有关 class 的所有东西。

class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)

data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where
Red == Red = True
Green == Green = True
Yellow == Yellow = True
_ == _ = False

class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool

instance Show TrafficLight where
show Red = "Red light"
show Yellow = "Yellow light"
show Green = "Green light"

ghci> Red == Red
True
ghci> Red == Yellow
False
ghci> Red elem [Red, Yellow, Green]
True
ghci> [Red, Yellow, Green]
[Red light,Yellow light,Green light]

class (Eq a) => Num a where
...

class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)

instance Eq Maybe where
...
instance Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False

instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False

## yes-no typeclass

class YesNo a where
yesno :: a -> Bool

YesNo typeclass 定义了一个函数。这个函数接受一个可以判断为真否的型别的值。而从我们写 a 的位置，可以看出来 a 必须是一个具体型别。

instance YesNo Int where
yesno 0 = False
yesno _ = True

instance YesNo [a] where
yesno [] = False
yesno _ = True

instance YesNo Bool where
yesno = id

instance YesNo (Maybe a) where
yesno (Just _) = True
yesno Nothing = False

instance YesNo (Tree a) where
yesno EmptyTree = False
yesno _ = True

instance YesNo TrafficLight where
yesno Red = False
yesno _ = True

ghci> yesno $length [] False ghci> yesno "haha" True ghci> yesno "" False ghci> yesno$ Just 0
True
ghci> yesno True
True
ghci> yesno EmptyTree
False
ghci> yesno []
False
ghci> yesno [0,0,0]
True
ghci> :t yesno
yesno :: (YesNo a) => a -> Bool

yesnoIf :: (YesNo y) => y -> a -> a -> a
yesnoIf yesnoVal yesResult noResult =
if yesno yesnoVal then yesResult else noResult

ghci> yesnoIf [] "YEAH!" "NO!"
"NO!"
ghci> yesnoIf [2,3,4] "YEAH!" "NO!"
"YEAH!"
ghci> yesnoIf True "YEAH!" "NO!"
"YEAH!"
ghci> yesnoIf (Just 500) "YEAH!" "NO!"
"YEAH!"
ghci> yesnoIf Nothing "YEAH!" "NO!"
"NO!"

## Functor typeclass

class Functor f where
fmap :: (a -> b) -> f a -> f b

instance Functor [] where
fmap = map

map :: (a -> b) -> [a] -> [b]
ghci> fmap (*2) [1..3]
[2,4,6]
ghci> map (*2) [1..3]
[2,4,6]

instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing

ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something serious.")
Just "Something serious. HEY GUYS IM INSIDE THE JUST"
ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") Nothing
Nothing
ghci> fmap (*2) (Just 200)
Just 400
ghci> fmap (*2) Nothing
Nothing

instance Functor Tree where
fmap f EmptyTree = EmptyTree
fmap f (Node x leftsub rightsub) =
Node (f x) (fmap f leftsub) (fmap f rightsub)
ghci> fmap (*2) EmptyTree
EmptyTree
ghci> fmap (*4) (foldr treeInsert EmptyTree [5,7,3,2,1,7])
Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree

Either a b 又如何？他可以是一个 functor 吗？Functor 限制型别构造子只能接受一个型别参数，但 Either 却接受两个。聪明的你会想到我可以 partial apply Either，先喂给他一个参数，并把另一个参数当作 free parameter。来看看 Either a 在标准函式库中是如何被定义的：

instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x

data Either a b = Left a | Right b

Data.Map 中的 Map 也可以被定义成 functor，像是 Map k v 的情况下，fmap 可以用 v -> v' 这样一个函数来 map over Map k v，并回传 Map k v'

## Kind

ghci> :k Int
Int :: *

ghci> :k Maybe
Maybe :: * -> *

Maybe 的型别构造子接受一个具体型别（像是 Int）然后回传一个具体型别，像是 Maybe Int。这就是 kind 告诉我们的资讯。就像 Int -> Int 代表这个函数接受 Int 并回传一个 Int* -> * 代表这个型别构造子接受一个具体型别并回传一个具体型别。我们再来对 Maybe 套用型别参数后再看看他的 kind 是什么：

ghci> :k Maybe Int
Maybe Int :: *

ghci> :k Either
Either :: * -> * -> *

ghci> :k Either String
Either String :: * -> *
ghci> :k Either String Int
Either String Int :: *

class Functor f where
fmap :: (a -> b) -> f a -> f b

class Tofu t where
tofu :: j a -> t a j

data Frank a b  = Frank {frankField :: b a} deriving (Show)

ghci> :t Frank {frankField = Just "HAHA"}
Frank {frankField = Just "HAHA"} :: Frank [Char] Maybe
ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
ghci> :t Frank {frankField = "YES"}
Frank {frankField = "YES"} :: Frank Char []

instance Tofu Frank where
tofu x = Frank x
ghci> tofu (Just 'a') :: Frank Char Maybe
Frank {frankField = Just 'a'}
ghci> tofu ["HELLO"] :: Frank [Char] []
Frank {frankField = ["HELLO"]}

data Barry t k p = Barry { yabba :: p, dabba :: t k }

ghci> :k Barry
Barry :: (* -> *) -> * -> * -> *

instance Functor (Barry a b) where
fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}