Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>What you need to express is that <code>f</code>, like <code>reduce</code> used in <code>reduceSig</code>, can be applied to <em>any</em> type that is an instance of <code>C</code>, as opposed to the current type, where <code>f</code> works on a single type that is an instance of <code>C</code>. This can be done like so:</p> <pre><code>mapsig :: (forall a. (C a) =&gt; a -&gt; a) -&gt; Signal -&gt; Signal mapsig f (Signal sig) = Signal (map (second f) sig) </code></pre> <p>You'll need the <code>RankNTypes</code> extension, as you often do when using existential types; note that the <em>implementation</em> of <code>mapsig</code> is the same, the type has just been generalised.</p> <p>Basically, with this type, <code>mapsig</code> gets to decide which <em>a</em> the function is called on; with your previous type, the <em>caller</em> of <code>mapsig</code> gets to decide that, which doesn't work, because only <code>mapsig</code> knows the correct <em>a</em>, i.e. the one inside the <code>Signal</code>.</p> <p>However, <code>mapsig reduce</code> does not work, for the obvious reason that <code>reduce :: (C a) =&gt; a -&gt; Int</code>, and you don't know that <em>a</em> is Int! You need to give <code>mapsig</code> a more general type (with the same implementation):</p> <pre><code>mapsig :: (C b) =&gt; (forall a. (C a) =&gt; a -&gt; b) -&gt; Signal -&gt; Signal </code></pre> <p>i.e., <code>f</code> is a function taking <em>any</em> type that is an instance of <code>C</code>, and producing a type that is an instance of <code>C</code> (that type is fixed at the time of the <code>mapsig</code> call and chosen by the caller; i.e. while the value <code>mapsig f</code> can be called on any Signal, it will always produce a Signal with the same <em>a</em> as a result (not that you can inspect this from outside).)</p> <p>Existentials and rank-N types are very tricky indeed, so this might take a bit of time to digest. :)</p> <hr> <p>As an addendum, it's worth pointing out that if all the functions in <code>C</code> look like <code>a -&gt; r</code> for some r, then you would be better off creating a <em>record</em> instead, i.e. turning</p> <pre><code>class C a where reduce :: a -&gt; Int foo :: a -&gt; (String, Double) bar :: a -&gt; ByteString -&gt; Magic data Signal = forall a. (C a) =&gt; Signal [(Double, a)] mapsig :: (C b) =&gt; (forall a. (C a) =&gt; a -&gt; b) -&gt; Signal -&gt; Signal </code></pre> <p>into</p> <pre><code>data C = C { reduce :: Int , foo :: (String, Double) , bar :: ByteString -&gt; Magic } data Signal = Signal [(Double, C)] mapsig :: (C -&gt; C) -&gt; Signal -&gt; Signal </code></pre> <p>These two Signal types are actually equivalent! The benefits of the former solution only appear when you have other data types that use <code>C</code> <em>without</em> existentially quantifying it, so that you can have code that uses special knowledge and operations of the specific instance of <code>C</code> it's using. If your primary use-cases of this class are through existential quantification, you probably don't want it in the first place. But I don't know what your program looks like :)</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload