George Tian
George Tian

Reputation: 441

How to change/extract the value of part of a manually defined datatype?

Say I have the following datatype:

data MyType = Element1 (Maybe Int) | Element2 (Maybe Int)
instance1 = Element1 (Just 1)
instance2 = function(instance1)

What can I use in place of function with so that instance2 = Element1 (Just 2)?

Upvotes: 1

Views: 99

Answers (3)

bwroga
bwroga

Reputation: 5459

You could define your data type using record syntax:

data MyType = Element1 { x :: (Maybe Int) } | Element2 { x :: (Maybe Int)}
instance1 = Element1 (Just 1)
instance2 = instance1 { x = Just 2 }

Upvotes: 1

Daniel Wagner
Daniel Wagner

Reputation: 153162

You give three equations:

instance1 = Element1 (Just 1)
instance2 = function(instance1)
instance2 = Element1 (Just 2)

Let's use these equations to solve for function.

function (instance1) = instance2
function (Element1 (Just 1)) = instance2
function (Element1 (Just 1)) = Element1 (Just 2)

This final line:

function (Element1 (Just 1)) = Element1 (Just 2)

is a perfectly good chunk of Haskell code that defines a function named function. Of course, it doesn't say what to do with things that are actually Element2s, or that are Element1s containing Nothings, or that are Element1s containing Justs containing things that aren't 1! But it does meet the specification given by the three original equations, and I think is an excellent demonstration of the concept of "equational reasoning" that Haskell shares with mathematics.

Upvotes: 4

chepner
chepner

Reputation: 532053

If you generlize your type slightly, you can make it a functor:

{-# LANGUAGE DeriveFunctor #-}
data MyType a = Element1 (Maybe a) | Element2 (Maybe a) deriving (Functor, Show)

Then the function you are looking for is (<$), from the Functor typeclass.

> i1 = Element1 (Just 1)
> i1
Element1 (Just 1)
> 2 <$ i1
Element1 (Just 2)

If you don't want to use the DeriveFunctor extension, the instance is trivial to define manually.

instance Functor MyType where
    fmap f (Element1 x) = Element1 (fmap f x)
    fmap f (Element2 x) = Element2 (fmap f x)

A specialized version of (<$) for a monomorphic type might look like

data MyType = Element1 (Maybe Int) | Element2 (Maybe Int)

change :: Int -> MyType -> MyType
change new (Element1 old) = Element1 (new <$ old)
change new (Element2 old) = Element2 (new <$ old)

Or, you might define your type as a product, to generalize the function over the data constructors.

data MyTypeTags = Element1 | Element2
data MyType = MyType MyTypeTags (Maybe Int)

change :: Int -> MyType -> MyType
change new (MyType tag old) = MyType tag (new <$ old)

Upvotes: 3

Related Questions