Подтвердить что ты не робот

Можно ли реализовать эту функциональность с помощью системы типа Haskell?

В Scala операции более высокого порядка в коллекциях всегда возвращают наилучший тип в контексте. Например, в случае BitSet, если вы сопоставляете int с ints, вы получаете BitSet, но если вы сопоставляете int с строками, вы получаете общий Set. Аналогично, если вы map a map с функцией, которая дает пару, то вы получите map взамен. Иначе вы получите простой Iterable. Как статический тип, так и представление результата выполнения во время выполнения зависят от типа результата передаваемой ему функции.

scala> Map(2 -> 'a', 6 -> 'b') map { case (k, v) => (k + 1, v.toString) }
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map(3 -> a, 7 -> b)

scala> Map(2 -> 'a', 6 -> 'b') map { _._1 }
res1: scala.collection.immutable.Iterable[Int] = List(2, 6)

scala> import collection.immutable.BitSet
import collection.immutable.BitSet

scala> BitSet(2, 44, 93).map(1 +)
res3: scala.collection.immutable.BitSet = BitSet(3, 45, 94)

scala> BitSet(2, 44, 93).map(_ + "hola")
res4: scala.collection.immutable.Set[String] = Set(2hola, 44hola, 93hola)

Возможно ли реализовать ту же функциональность в системе типа Haskell? Если да, то как? Был бы высоко оценен перевод примеров Haskell в приведенный выше фрагмент кода.: -)

4b9b3361

Ответ 1

Я не думаю, что ваш первый пример - очень Haskell-y, поскольку вы перегружаете одно и то же имя, чтобы делать две очень разные вещи. В Haskell при сопоставлении функции над некоторым контейнером вы ожидаете получить тот же тип контейнера. На самом деле, это настолько распространено в Haskell, что существует класс типа Functor, который инкапсулирует этот шаблон.

Все формы перегрузки в Haskell сводятся к использованию классов типов, и, хотя вы можете использовать их для достижения чего-то подобного, это было бы очень надуманным и не очень полезным, просто используя простые функции, которые делают только одно, что вы хотите.

Prelude> import qualified Data.Map as M
Prelude Data.Map> let m = M.fromList [(2, 'a'), (6, 'b')]
Prelude Data.Map> M.map show $ M.mapKeys (+1) m
fromList [(3,"'a'"),(7,"'b'")]
Prelude Data.Map> M.keys m
[2,6]

Я думаю, что ваш второй пример более уместен для Haskell, так как он больше подходит для выбора наиболее эффективной реализации структуры данных на основе скрытого типа, и я подозреваю, что это не должно быть слишком сложно сделать, используя типа семейства, но я не слишком знаком с ними, поэтому я позволю кому-то еще попытаться ответить на эту часть.

Ответ 2

Я очень согласен с хаммаром, но вот способ сделать это, вроде:

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances #-}

import Prelude hiding (map)

import qualified Data.Map as M
import qualified Data.IntSet as I
import qualified Data.Set as S

type family Elem c :: *
type instance Elem (M.Map k v) = (k, v)
type instance Elem I.IntSet = Int
type instance Elem (S.Set a) = a

class Map c o where
  type Result c o :: *
  map :: (Elem c -> o) -> c -> Result c o

instance Map I.IntSet Int where
  type Result I.IntSet Int = I.IntSet
  map = I.map

instance Map I.IntSet String where
  type Result I.IntSet String = S.Set String
  map f = S.fromList . fmap f . I.toList

instance (Ord k, Ord k1) => Map (M.Map k v) (k1, v1) where
  type Result (M.Map k v) (k1, v1) = M.Map k1 v1
  map f = M.fromList . fmap f . M.toList

instance (Ord k) => Map (M.Map k v) Int where
  type Result (M.Map k v) Int = [Int]
  map f = fmap f . M.toList

Здесь он находится в действии:

*Main> map fst (M.fromList [(2::Int, 'a'), (6, 'b')])
[2,6]
*Main> map (\(k, v) -> (k + 1, show v)) (M.fromList [(2, 'a'), (6, 'b')])
fromList [(3,"'a'"),(7,"'b'")]
*Main> :t it
it :: M.Map Integer [Char]

В идеале вы хотели бы сделать это:

instance (Ord k) => Map (M.Map k v) a where
  type Result (M.Map k v) a = [a]
  map f = fmap f . M.toList

Но этот экземпляр конфликтует с парным. Таким образом, нет хорошего способа предоставить экземпляр для каждого другого типа.

Ответ 3

Добавление в hammar: Я думаю, что второй пример невозможен, поскольку он содержит неявные преобразования типов.

Игнорируя это для обсуждения, как выглядела бы подпись типа:

setmap :: (Set a, Set b) => a e -> (e -> f) -> b f 

Итак, да, это возможно, но с условием, что может потребоваться указать тип возврата.