Это в основном тот же механизм, что и для команды без запроса с:
CommandType.StoredProcedure
Существует множество примеров, первый Google возвращает это
Есть также небольшая ловушка, в которую вы можете попасть, если ваш SP является функцией, ваш параметр возвращаемого значения должен быть первым в коллекции параметров
Если вам действительно нужно это сделать, используйте экзистенциальный :
{-# LANGUAGE GADTs #-}
class IsShape a where
draw :: a -> IO ()
data Rectangle = Rectangle Int Int
instance IsShape Rectangle where
draw (Rectangle length width) = ...
data Circle = Circle Int Int
instance IsShape Circle where
draw (Circle center radius) = ...
data Shape where
Shape :: IsShape a => a -> Shape
shapes = [Shape (Circle 5 10), Shape (Circle 20 30), Shape (Rectangle 10 15)]
(я переименовал ваш класс, так как в противном случае будет конфликт имени с типом данных, и такое именование кажется более естественным.)
Преимущество этого решения перед другим ответом, включающим один тип данных с разными конструкторами, состоит в том, что оно открыто ; вы можете определять новые экземпляры IsShape
где угодно. Преимущество другого ответа состоит в том, что он более «функциональный», а также в том, что закрытость в некоторых случаях может быть преимуществом, поскольку это означает, что клиенты точно знают, чего ожидать.
Рассмотрите возможность использования одного типа вместо отдельных типов и класса типов.
data Shape = Rectangle Int Int
| Circle Int Int
draw (Rectangle length width) = ...
draw (Circle center radius) = ...
shapes = [Circle 5 10, Circle 20 30, Rectangle 10 15]
Как сказал Ганеш, вы действительно можете использовать GADT для большей безопасности типов. Но если вы этого не хотите (или не нуждаетесь), вот мой вариант:
Как вы уже знаете, все элементы списка должны быть одного типа. Не очень полезно иметь список элементов разных типов, потому что тогда вы выбрасываете информацию о своем типе.
Однако в этом случае, поскольку вы хотите выбросить информацию о типе (вас просто интересует рисованная часть значения), вы бы предложили изменить тип ваших значений на что-то, что можно просто рисовать.
type Drawable = IO ()
shapes :: [Drawable]
shapes = [draw (Circle 5 10), draw (Circle 20 30), draw (Rectangle 10 15)]
Предположительно, ваш фактический Drawable
будет чем-то более интересным, чем просто IO ()
(может быть, что-то вроде: MaxWidth -> IO ()
).
А также, из-за ленивой оценки фактическое значение выиграло ' t рисоваться до тех пор, пока вы не зададите список с чем-то вроде sequence_
. Так что вам не нужно беспокоиться о побочных эффектах (но вы, вероятно, уже видели это по типу фигур
).
Для полноты (и включите мой комментарий в этот ответ): Это более общая реализация, полезная, если Shape
имеет больше функций:
type MaxWith = Int
class Shape a where
draw :: a -> MaxWidth -> IO ()
size :: a -> Int
type ShapeResult = (MaxWidth -> IO (), Int)
shape :: (Shape a) => a -> ShapeResult
shape x = (draw x, size x)
shapes :: [ShapeResult]
shapes = [shape (Circle 5 10), shape (Circle 20 30), shape (Rectangle 10 15)]
Здесь функция shape
преобразует значение Shape a
в ShapeResult
, просто вызывая все функции в классе Shape
. Из-за лени ни одно из значений фактически не вычисляется, пока они вам не понадобятся.
Честно говоря, я не думаю, что на самом деле использовал бы такую конструкцию. Я бы либо использовал метод Drawable
, указанный выше, либо, если требуется более общее решение, использовать GADT. При этом это забавное упражнение.
Один из способов сделать это - использовать vtables:
data Shape = Shape {
draw :: IO (),
etc :: ...
}
rectangle :: Int -> Int -> Shape
rectangle len width = Shape {
draw = ...,
etc = ...
}
circle :: Int -> Int -> Shape
circle center radius = Shape {
draw = ...,
etc = ...
}