在Haskell中定义数据结构的建议

Sno*_*all 17 haskell

我在Haskell中建模数据结构时遇到了麻烦.假设我正在运行一个动物研究设施,我想跟踪我的老鼠.我想跟踪大鼠到笼子和实验的分配.我还想跟踪我的老鼠的体重,我的笼子的体积,并记录我的实验.

在SQL中,我可能会这样做:

create table cages (id integer primary key, volume double);
create table experiments (id integer primary key, notes text)
create table rats (
    weight double,
    cage_id integer references cages (id),
    experiment_id integer references experiments (id)
);
Run Code Online (Sandbox Code Playgroud)

(我意识到这允许我将来自不同实验的两只老鼠分配到同一个笼子里.这是有意的.我实际上并没有运行动物研究设施.)

必须可能做的两项操作:(1)给老鼠,找到它的笼子的体积,和(2)给老鼠,得到它所属的实验的注释.

在SQL中,那些将是

select cages.volume from rats
  inner join cages on cages.id = rats.cage_id
  where rats.id = ...; -- (1)
select experiments.notes from rats
  inner join experiments on experiments.id = rats.experiment_id
  where rats.id = ...; -- (2)
Run Code Online (Sandbox Code Playgroud)

我如何在Haskell中建模这个数据结构?


一种方法是

type Weight = Double
type Volume = Double

data Rat = Rat Cage Experiment Weight
data Cage = Cage Volume
data Experiment = Experiment String

data ResearchFacility = ResearchFacility [Rat]

ratCageVolume :: Rat -> Volume
ratCageVolume (Rat (Cage volume) _ _) = volume

ratExperimentNotes :: Rat -> String
ratExperimentNotes (Rat _ (Experiment notes) _) = notes
Run Code Online (Sandbox Code Playgroud)

但是这个结构不会引入一堆Cages和Experiments 的副本吗?或者我应该不担心它并希望优化器能够解决这个问题?

Dan*_*ner 16

这是我用于测试的简短文件:

type Weight = Double
type Volume = Double

data Rat = Rat Cage Experiment Weight deriving (Eq, Ord, Show, Read)
data Cage = Cage Volume               deriving (Eq, Ord, Show, Read)
data Experiment = Experiment String   deriving (Eq, Ord, Show, Read)

volume     = 30
name       = "foo"
weight     = 15
cage       = Cage volume
experiment = Experiment name
rat        = Rat cage experiment weight
Run Code Online (Sandbox Code Playgroud)

然后我开始ghci和导入System.Vacuum.Cairo,从令人愉快的vacuum-cairo包装.

*Main System.Vacuum.Cairo> view (rat, Rat (Cage 30) (Experiment "foo") 15)
Run Code Online (Sandbox Code Playgroud)

不共享

*Main System.Vacuum.Cairo> view (rat, Rat (Cage 30) experiment 15)
Run Code Online (Sandbox Code Playgroud)

共享实验

(我不确定为什么在这个箭头中有双倍的箭头,但是你可以忽略/折叠它们.)

*Main System.Vacuum.Cairo> view (rat, Rat cage experiment weight)
Run Code Online (Sandbox Code Playgroud)

共享ARGS

*Main System.Vacuum.Cairo> view (rat, rat)
Run Code Online (Sandbox Code Playgroud)

共享所有

*Main System.Vacuum.Cairo> view (rat, Rat cage experiment (weight+1))
Run Code Online (Sandbox Code Playgroud)

共享改性

如上所述,经验法则是在调用构造函数时准确创建新对象; 否则,如果您只是命名已创建的对象,则不会创建新对象.这在Haskell中是安全的,因为它是一种不可变的语言.


Dan*_*kov 6

您的模型的更自然的Haskell表示将是笼子包含实际的鼠标对象而不是它们的ID:

data Rat = Rat RatId Weight
data Cage = Cage [Rat] Volume
data Experiment = Experiment [Rat] String
Run Code Online (Sandbox Code Playgroud)

然后,您将ResearchFacility使用智能构造函数创建对象,以确保它们遵循规则.它看起来像:

research_facility :: [Rat] -> Map Rat Cage -> Map Rat Experiment -> ResearchFacility
research_facility rats cage_assign experiment_assign = ...
Run Code Online (Sandbox Code Playgroud)

其中cage_assignexperiment_assign是包含与sql中的cage_idexperiment_id外键相同的信息的映射.