В синтаксисе Haskell мы можем иметь (абстрактный) тип, такой как [a -> b]
, который представляет собой список функций от a до b. Конкретным типом этого будет [Int -> Int]
, например map (*) [1..10]
. Возможно ли иметь список каскадных функций в типе типа [a -> b, b -> c, c -> d, ...]
? Отдельные элементы списка все разные (я думаю), поэтому я не думаю, что это возможно. Но возможно ли это с зависимыми типами? Какова была бы его подпись типа (предпочтительно в синтаксисе псевдо-Хаскелла)?Каким будет тип списка каскадных функций?
ответ
Вы не можете сделать это с помощью простого списка, но вы можете построить свой собственный список, как тип следующим образом:
{-# LANGUAGE GADTs #-}
data CascadingList i o where
Id :: CascadingList i i
Cascade :: (b -> o) -> CascadingList i b -> CascadingList i o
Тогда вы могли бы сделать эти CascadingList
S следующим образом:
addOnePositive :: CascadingList Int Bool
addOnePositive = Cascade (>0) $ Cascade (+1) $ Id
вы могли бы 'коллапс' списки:
collapse :: CascadingList a b -> a -> b
collapse Id = id
collapse (Cascade f c) = f . collapse c
Тогда вы бы
collapse addOnePositive 0 == True
Обратите внимание, что это не учитывает типы промежуточных функций, поэтому, возможно, это не то, что вы ищете.
Я просто понял, что это ближе к чему-то вроде [с -> d, Ь -> с, а -> Ь]. Это легкое изменение, чтобы приблизить его к вашим намерениям; Я мог бы его отредактировать, но, думаю, вы поняли.
Как я уже отмечал в своем [ответе на предыдущий вопрос] (http://stackoverflow.com/a/26566362/1186208), последующий вопрос (и наблюдение) таков: что такая коллекция дает вам функцию состав? (Такая же конструкция с другой «категорией» может быть другим вопросом ...) –
Одно потенциальное преимущество, которое я вижу, состоит в том, что вы можете извлекать скомпонованные функции из такого типа структуры, но это не так с простой композицией функций. Надуманный пример: '' (+1). (-1) == (-1). (+1) '', но '' [(+1), (- 1)]/= [(-1), (+ 1)] '' (явно злоупотребляющая запись). –
Да, я тоже это заметил. Но вы не можете с ними справиться; среди прочего, вы не можете предсказать их типы за пределами GADT. –
небольшое улучшение на ответ scrambledeggs', обращаясь к некоторым из комментариев:
{-# LANGUAGE GADTs #-}
import Data.Typeable
data CascadingList i o where
Id :: CascadingList i i
Cascade :: Typeable b => (b -> o) -> CascadingList i b -> CascadingList i o
Теперь, когда вы подходите картины на Cascade
, вы можете по крайней мере попытаться угадать, какой тип b
является использование the eqT
and cast
functions from Data.Typeable
, и если вы догадываетесь, что вы действительно можете использовать внутренние функции. Мягкий минус - он работает только для типов, имеющих экземпляр Typeable
(который, по крайней мере, может получить GHC).
Использования DataKinds
, вы можете выставить внутренние типы коллекции, которые могут сделать с помощью составных частей проще:
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
module Cascade where
import Control.Monad ((>=>), liftM)
import Control.Category ((>>>))
data Cascade (cs :: [*]) where
End :: Cascade '[a]
(:>>>) :: (a -> b) -> Cascade (b ': cs) -> Cascade (a ': b ': cs)
infixr 5 :>>>
-- a small example
fs :: Cascade '[ String, Int, Float ]
fs = read :>>> fromIntegral :>>> End
-- alternate using functions from one chain then the other
zigzag :: Cascade as -> Cascade as -> Cascade as
zigzag End End = End
zigzag (f :>>> fs) (_ :>>> gs) = f :>>> zigzag gs fs
-- compose a chain into a single function
compose :: Cascade (a ': as) -> a -> Last (a ': as)
compose End = id
compose (f :>>> fs) = f >>> compose fs
-- generalizing Either to a union of multiple types
data OneOf (cs :: [*]) where
Here :: a -> OneOf (a ': as)
There :: OneOf as -> OneOf (a ': as)
-- start the cascade at any of its entry points
fromOneOf :: Cascade cs -> OneOf cs -> Last cs
fromOneOf fs (Here a) = compose fs a
fromOneOf (_ :>>> fs) (There o) = fromOneOf fs o
-- generalizing (,) to a product of multiple types
data AllOf (cs :: [*]) where
None :: AllOf '[]
(:&) :: a -> AllOf as -> AllOf (a ': as)
infixr 5 :&
-- end the cascade at all of its exit points
toAllOf :: Cascade (a ': as) -> a -> AllOf (a ': as)
toAllOf End a = a :& None
toAllOf (f :>>> fs) a = a :& toAllOf fs (f a)
-- start anywhere, and end everywhere after that
fromOneOfToAllOf :: Cascade cs -> OneOf cs -> OneOf (Map AllOf (Tails cs))
fromOneOfToAllOf fs (Here a) = Here $ toAllOf fs a
fromOneOfToAllOf (_ :>>> fs) (There o) = There $ fromOneOfToAllOf fs o
-- type level list functions
type family Map (f :: a -> b) (as :: [a]) where
Map f '[] = '[]
Map f (a ': as) = f a ': Map f as
type family Last (as :: [*]) where
Last '[a] = a
Last (a ': as) = Last as
type family Tails (as :: [a]) where
Tails '[] = '[ '[] ]
Tails (a ': as) = (a ': as) ': Tails as
-- and you can do Monads too!
data CascadeM (m :: * -> *) (cs :: [*]) where
EndM :: CascadeM m '[a]
(:>=>) :: (a -> m b) -> CascadeM m (b ': cs) -> CascadeM m (a ': b ': cs)
infixr 5 :>=>
composeM :: Monad m => CascadeM m (a ': as) -> a -> m (Last (a ': as))
composeM EndM = return
composeM (f :>=> fs) = f >=> composeM fs
fromOneOfM :: Monad m => CascadeM m cs -> OneOf cs -> m (Last cs)
fromOneOfM fs (Here a) = composeM fs a
fromOneOfM (_ :>=> fs) (There o) = fromOneOfM fs o
-- end the cascade at all of its exit points
toAllOfM :: Monad m => CascadeM m (a ': as) -> a -> m (AllOf (a ': as))
toAllOfM EndM a = return $ a :& None
toAllOfM (f :>=> fs) a = do
as <- toAllOfM fs =<< f a
return $ a :& as
-- start anywhere, and end everywhere after that
fromOneOfToAllOfM :: Monad m => CascadeM m cs -> OneOf cs -> m (OneOf (Map AllOf (Tails cs)))
fromOneOfToAllOfM fs (Here a) = Here `liftM` toAllOfM fs a
fromOneOfToAllOfM (_ :>=> fs) (There o) = There `liftM` fromOneOfToAllOfM fs o
Я думаю, что 'Chain' также может быть реализован как (закрытое) семейство типов, потому что параметр типа' cs' точно определяет, какие конструкторы будут использоваться. –
Christian Conkle: Да, я сделал это [здесь для 'OneOf'] (http://stackoverflow.com/questions/25414521/types-for-parser-combinators). Теперь я обезьяна. – rampion
Christian Conkle: На данный момент я отказался от семей с закрытыми типами, так как продолжал получать инъекционные ошибки, когда пытался сделать что-нибудь интересное. – rampion
Вы не можете сделать это с помощью простых списков в Haskell, но это возможно. Посмотрите на библиотеку HList для гетерогенных списков. Будьте предупреждены, что для получения такого динамического поведения существует множество расширений, используемых этой библиотекой. – bheklilr
Посмотрите на http: //stackoverflow.com/questions/26565306/how-to-define-a-multiple-composition-function .... – jamshidh
Это дубликат (подмножество) связанного вопроса jamshidh. Однако этот вопрос прямо говорит о проблеме. –