说我有一个任务,它表示从k
到v
的一些计算,其中一些输入必须从外部获取。
newtype Task k v = Task { run ∷ ∀ f. Monad f ⇒ (k → f v) → f v }
对于某些任务,将使用mapM
,例如获取多个密钥。我想将mapM
专用于某些monad。专门针对IO
单子,我想使用Control.Concurrent.Async.mapConcurrently
同时执行IO操作。
我的第一个直觉是引入包装类型
newtype AsyncIO a = AsyncIO { io :: IO a }
然后介绍
instance Monad AsyncIO
但是,这不起作用,因为在当前的GHC实现中,mapM
是用traverse
的{{1}}定义的。
是否有一个优雅的解决方案?
答案 0 :(得分:5)
嗯,from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declared_attr, declarative_base
from sqlalchemy.schema import PrimaryKeyConstraint
from sqlalchemy import Sequence, create_engine
class Base:
@declared_attr
def __tablename__(cls):
return f"{cls.__name__}"
@declared_attr
def seq(cls):
return Sequence("test_1", start=1, increment=1)
@declared_attr
def id(cls):
return Column(Integer, cls.seq, unique=True, autoincrement=True, primary_key=True)
Base = declarative_base(cls=Base)
def relate(model, x):
"""Model is the original class, x is what class needs to be as
an attribute for model"""
attributeName = x.__tablename__
idAttributeName = "{}Id".format(attributeName)
setattr(model, idAttributeName,
Column(ForeignKey(x.id)))
setattr(model, attributeName,
relationship(x,
foreign_keys=getattr(model, idAttributeName),
primaryjoin=getattr(
model, idAttributeName) == x.id,
remote_side=x.id
)
)
return model.__table__.c[idAttributeName]
def possibleSolution(model):
if len(model.defined):
newPriCols = []
for x in model.defined:
newPriCols.append(relate(model, x))
for priCol in model.__table__.primary_key:
priCol.primary_key = False
priCol.nullable = True
model.__table__.primary_key = PrimaryKeyConstraint(
*newPriCols
# TODO: ADD all the columns that are in the model that are also a primary key
# *[col for col in model.__table__.c if col.primary_key]
)
class A_Table(Base):
pass
class B_Table(Base):
defined = [A_Table]
possibleSolution(B_Table)
engine = create_engine('sqlite://')
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()
a = A_Table()
b = B_Table(A_TableId=a.id)
print(B_Table.__table__.primary_key)
session.add(a)
session.commit()
session.add(b)
session.commit()
只需要一个traverse
。通过为Applicative
使用备用mapM
,可以使Applicative
并行执行其操作(这是IO
的实现方式)。但是,此mapConcurrently
没有合法的Applicative
实例:Monad
,而其他(>>=)
操作将与Monad
和其他{{1 }}操作。例如。 (<*>)
不等同于Applicative
,因为mf >>= \f -> mx >>= \x -> return (f x)
不能并行执行其参数,但是mf <*> mx
可以并行执行。 (您可能可以使用(>>=)
创建一个有效的(<*>)
实例,但是Monad
也可以。)
您可以做的一件事情是将unsafeInterleaveIO
的函子unsafeInterleaveIO
与Task
分开传递,然后提供自然转换以将前者中的所有计算注入到后者。查找功能也应该在Applicative
上下文中。
Monad
如果您不涉及任何特殊的Applicative
,只需使用newtype Task k v = Task { run ∷ ∀f f'. (Monad f, Applicative f')
⇒ (∀a. f' a → f a)
→ (k → f' v) → f v
}
作为自然转换:
Applicative
对于id
,特殊的runSimple ∷ Monad f ⇒ Task k v → (k → f v) → f v
runSimple t = run t id
仿函数已经在IO
中为您打包好了:
Applicative
您将这样写一个Control.Concurrent.Async.Concurrently
:
runWithParallelIO ∷ Task k v → (k → IO v) → IO v
runWithParallelIO t lookup = run t runConcurrently (Concurrently . lookup)
如果您发现自己编写的Task
并不能从单独的task ∷ Task _k _v
task = Task go
where go exec lookup = do _
xs <- exec $ mapM lookup _
_
和Task
上下文中受益,则可以使用此智能构造函数
Monad
避免将Applicative
中的每个taskSimple ∷ (∀f. Monad f ⇒ (k → f v) → f v) → Task k v
taskSimple r = Task (\exec lookup -> r (exec . lookup))
包装起来。 AFAICT,lookup
和exec
是幂等的。
答案 1 :(得分:0)
尽管我不确定后者是否会与f的量化相冲突,但可以将mapM
或mapConcurrently
作为附加参数,或者尽可能少地将其用作隐式参数。