2015-08-06 2 views
2

В этой игре я хочу иметь классы игроков, уровни игроков и корабли, которые могут иметься по классу и уровню. У вас может быть только один корабль за раз. Я пришел к выводу, что мне нужны семейства datakinds и data.Функциональный банан-путешественник - система корабля

Вот фрагмент того, что у меня есть, а затем мои вопросы.

{-# LANGUAGE DataKinds #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE KindSignatures #-} 
{-# LANGUAGE MultiParamTypeClasses} 
{-# LANGUAGE ConstraintKinds #-} 

data PlayerClass = Pirate | Merchant | SpaceNinja deriving Show 
data ShipClass = Tiny | Medium | Large deriving Show 
data Merchant = Rookie | Swindler | IndustryCaptain deriving Show 
data Pirate = Lubber | Matey | Captain deriving Show 

data family PShip (a :: PlayerClass) (b :: ShipClass) 
data instance PShip Pirate Tiny = Junk 
    { _hull_junk    :: Hull 
    , _bridge_junk   :: Bridge 
    , _cargo_compartment_junk :: CargoCompartment 
    , _fuel_tank_junk   :: Tank 
} deriving Show 

Я понимаю, что с DataKinds я могу продвигать термины к типам, но все равно использую их в качестве Условий. Я хочу, чтобы класс абстрагировал выполнение операций для PShips. Я хотел бы сдерживать на уровне уровня (PlayerClass), а затем создавать экземпляры для типов с таким типом. Вот фрагмент того, что я пытаюсь сделать, но не могу.

class (PShip a) => Ship a (b :: PlayerClass) where 
    ... 
instance Ship (Pirate Tiny) Lubber ... 

Имеет ли это смысл? Я на правильном пути, или мне нужно создать резервную копию и переосмыслить эту проблему? Я ясно сформулировал проблему?

+0

Напоминает мне https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/. – Zeta

+1

Я думаю о https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/type-families-and-pokemon;) – Carsten

+0

Зета: Я подозревал, что я был старше -engineering. Как вы думаете, я мог бы лучше использовать записи и функции? –

ответ

2

Не применяйте ограничение на «корабли, которые могут иметься по классу и уровню» на уровне типа. Это может быть OP, но есть (возможно) нет причин, по которым игра не должна работать с космическим ниндзя уровня 1, занимающим большой корабль линии 19-го века. Вместо того, чтобы прибегать к программированию на уровне, правила игры могут быть записаны как обычные функции или даже закодированы как данные.

Если вы хотите выбрать, какое судно кто-то использует со своего класса и размера судна, вы можете просто написать функцию.

data Ship = Ship { 
    _hull_junk    :: Hull, 
    _bridge_junk   :: Bridge, 
    _cargo_compartment_junk :: CargoCompartment, 
    _fuel_tank_junk   :: Tank 
} 

data Hull = Raft | Sloop | Brig | Schooner | Starfighter | ... 

playerShip :: PlayerClass -> ShipClass -> Ship 
playerShip Pirate Tiny = Ship { 
    _hull_junk = Sloop, 
    _bridge_junk = ... 
} 
playerShip SpaceNinja Tiny = Ship { 
    _hull_junk = Starfighter, 
    _bridge_junk = ... 
} 
... 

Мы переехали правила, о которых идет корабль с игроком и классом судна от уровня типа до уровня обычного кода на Haskell.

Вы указали, что есть несколько кораблей, которые могут иметь класс и уровень. Для того, чтобы справиться с этим мы бы вернуть список судов, основанных на классе игрока и размер судна

allowedShips :: PlayerClass -> ShipClass -> [Ship] 

Чтобы реализовать это легко, мы могли бы сделать файл данных игры, который включает в себя список кораблей, и для каждого судна , список классов игроков и классов кораблей, для которых он может быть использован. Поступая таким образом, мы переходим к правилам о том, какие корабли могут идти с игроком и классом корабля с уровня обычного кода Haskell до уровня данных.

[ 
    ([(Pirate, Tiny)], Ship { 
     _hull_junk = Sloop, 
     _bridge_junk = ... 
    }), 
    ([(SpaceNinja, Tiny)], Ship { 
     _hull_junk = Sloop, 
     _bridge_junk = ... 
    }), 
    ([(Pirate, Tiny),(Merchant, Tiny),(SpaceNinja,Tiny)], Ship { 
     _hull_junk = Raft, 
     _bridge_junk = ... 
    }), 
    ... 
] 

Для начала, вы можете перечислить данные, как это в .hs файле shipData :: [([(PirateClass, ShipClass)],Ship); shipData = ... и осуществлять allowedShips путем фильтрации shipData класса пиратского и класс судна.

Если все данные _junk являются данными, мы можем пойти дальше и обработать эти данные как данные, сохранить их в файле и прочитать его с помощью производных Read экземпляров.

+0

Хорошо, я сделаю это так. Я надеялся, что у меня есть прецедент, чтобы изучить программирование на уровне. –