我在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)
但是这个结构不会引入一堆Cage
s和Experiment
s 的副本吗?或者我应该不担心它并希望优化器能够解决这个问题?
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)
*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中是安全的,因为它是一种不可变的语言.
您的模型的更自然的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_assign
和experiment_assign
是包含与sql中的cage_id
和experiment_id
外键相同的信息的映射.