Suppose that you wish to have a safe foreign function that can only be used with instances of a given typeclass. For example:
moduleComputerToolswhereclassComputerfwherecompute::f->Intforeignimporttriplicate::forallc. (Computerc) =>c->Int
Though triplicate
takes only one argument in PureScript side, its definition in foreign side needs one more: a Computer
type-class dictionary (PureScript by Example 10.9)
// module ComputerTools exports.triplicate=function(aComputerDictionary){returnfunction(aComputerInstance){return3*aComputerDictionary.compute(aComputerInstance);};};
If you don't like relying on the runtime representation of type class dictionaries there is another option: you just should pass in the functions explicitly. Then you’d define a wrapper that actually takes the constraint. So instead of
foreignimporttriplicate::forallc. (Computerc) =>c->Int
you would do
-- This function is unsafe:-- c should be a Computer instance-- but the type checker can't verify thisforeignimporttriplicateImpl::foralla. (a->Int) ->a->Int-- now triplicate is not foreigntriplicate::forallc. (Computerc) =>c->Int triplicate = triplicateImpl compute
and then you’d have a non-foreign wrapper, that actually has the constraint, and you’d just pass in compute
to the foreign function.
But now someone could use the unsafe triplicateImpl
. There is a trivial solution for that problem: you don’t export the foreign function, you only export your wrapper that takes the constraint.
moduleComputerTools ( Computer , compute , triplicate ) whereclassComputerfwherecompute::f->IntforeignimporttriplicateImpl::foralla. (a->Int) ->a->Inttriplicate::forallc. (Computerc) =>c->Int triplicate = triplicateImpl compute
// module ComputerTools exports.triplicateImpl=function(shouldBeComputeFunction){returnfunction(shouldBeComputerInstance){return3*shouldBeComputeFunction(shouldBeComputerInstance);};};