假设我的数据类型定义为
data Foo = Hello Int | World Int
并作为
isWorld :: Foo -> Bool
isWorld (World i) = True
isWorld (Hello i) = False
getWorlds :: Set Foo -> Set World
getWorlds = Set.filter isWorld
这不起作用:
Not in scope: type constructor or class `World'
这是有道理的,因为World
只是一个函数,但我不知道如何在Haskell中对此进行建模。使用data
是否正确?我希望能够仅为某些实例定义函数:
foo :: World -> Int
foo (World i) = i
正如预期的那样,这会引发相同的错误,因为World
不是类型。有没有办法做到这一点,最好没有GHC扩展?
答案 0 :(得分:5)
实现此目的的最简单方法是简单地为每个构造函数的值创建一个类型。
newtype Hello = Hello Int deriving (Eq,Ord)
newtype World = World Int deriving (Eq,Ord)
type Foo = Either Hello World
getWorlds :: Set Foo -> Set World
getWorlds = Set.fromList . rights . Set.toList -- import Data.Either
答案 1 :(得分:4)
问题是你想要一个列表(编辑:或者更确切地说是一组但是相同的逻辑适用)两种可能的东西,而Haskell中的常规列表是归属的。但是,如果您像这样定义数据,则将一组单独的类型包装在另一种数据类型中:
public class Main {
public static void main(String[] args) {
Config config = new Config();
HazelcastInstance instance = Hazelcast.newHazelcastInstance(config);
final MapConfig customerMapConf = config.getMapConfig("customers");
final MapStoreConfig customerMapStoreConf = customerMapConf.getMapStoreConfig();
customerMapStoreConf.setImplementation(new SimpleMapLoader());
customerMapStoreConf.setEnabled(true);
customerMapConf.setMapStoreConfig(customerMapStoreConf);
config.addMapConfig(customerMapConf);
MapIndexConfig indexConfig = new MapIndexConfig();
indexConfig.setAttribute("name");
indexConfig.setOrdered(false);
customerMapConf.setMapIndexConfigs(Arrays.asList(indexConfig));
customerMapStoreConf.setInitialLoadMode(InitialLoadMode.LAZY);
Map<Integer, String> map = instance.getMap("customers");
// System.out.println(map.get("1"));
instance.shutdown();
}
}
你仍然可以:
newtype Hello = Hello Int
newtype World = World Int
data Foo = FooHello Hello
| FooWorld World
Foo
类型的函数过滤Foo
以实现Foo -> Bool
(作为示例)根据您的尝试,可能已经有一些库可以解决类似的问题。我认为至少one看似相关。
答案 2 :(得分:2)
在您的代码中World
不是类型,它只是一个构造函数。
Haskell中的惯用解决方案是使用Either
创建类型的并集:
newtype Hello = Hello { getHello :: Int } deriving (Ord, Eq)
newtype World = World { getWorld :: Int } deriving (Ord, Eq)
type Foo = Either Hello World
isWorld :: Foo -> Bool
isWorld = Data.Either.isRight
getWorlds :: Set Foo -> Set World
getWorlds = Set.fromDistinctAscList . Data.Either.rights . Set.toAscList
foo :: World -> Int
foo = getWorld
它适用于两个以上的构造函数
newtype Cheese = Cheese { getCheese :: Int }
newtype Bread = Bread { getBread :: Int }
newtype Wine = Wine { getWine :: Int }
type Comestible = Either Cheese (Either Bread Wine)
但如果你发现parens令人反感,你可以使用Either
infix:
{-# LANGUAGE TypeOperators #-}
-- ...
type Comestible = Cheese `Either` Bread `Either` Wine
-- (NOTE: this parses as Either (Either Cheese Bread) Wine
如果情况过于冗长,您可以申报自己的运营商:
type (+) = Either
infixr 5 +
type Comestible = Cheese + Bread + Wine
我在这里使用了+
,因为Either a b
是 sum 类型,而<{1>}是产品类型。
答案 3 :(得分:1)
您可以选择另外两个选项:
并且只处理各个替代品的内容。即你可以写
getWorlds :: Set Foo -> Set Int
getWorlds = Set.mapMonotonic (\(World i)->i) $ Set.filter isWorld
data
类型... 但限制哪些替代品可以通过类型约束:
{-# LANGUAGE GADTs, DataKinds, KindSignatures #-}
data Foo (a :: FooC) where
Hello :: CanHello a => Int -> Foo a
World :: CanWorld a => Int -> Foo a
data FooC = FooC | HelloC | WorldC
class CanHello (a :: FooC)
instance CanHello 'FooC
instance CanHello 'HelloC
class CanWorld (a :: FooC)
instance CanWorld 'FooC
instance CanWorld 'WorldC
此时,您有Foo
的三种不同“风味”:
Foo' 'FooC
与原始类型类似:其值可以是Hello
或World
,因为FooC
是两个类的实例。Foo' 'WorldC
与World
类型的含义相同:其构造函数World
与Foo
的构造函数相同,但它不允许Hello
构造函数。Foo' 'HelloC
只有允许Hello
构造函数。所以你现在可以给他们起名字......
type Foo = Foo' 'FooC
type World = Foo' 'WorldC
type Hello = Foo' 'HelloC
然后写
getWorlds :: Set Foo -> Set World
getWorlds = Set.mapMonotonic (\(World i)->World i) $ Set.filter isWorld
函数\(World i) -> World i
看起来多余,但实际上是必要的:你将数据包装在一个新的约束中,实际上它见证了它总是World
。