(Contexte: J'ai réfléchi à faire une présentation sur la programmation F # et fonctionnelle. De l'expérience, je pense que le facteur «WOW» de la correspondance et de la déduction du type n'est pas nécessairement suffisant pour contrer l'aide » ! «Facteur de» Où sont mes supports et des points-virgules frisés, mon code va tomber du bord! ». Ce qui m'a fait penser au véritable facteur wow - pour moi - qui est 1) que s'il compile, cela signifie généralement que cela signifie que que cela fonctionne et 2) que vous pouvez souvent déduire la mise en œuvre des types) em> p>
Il y a un vidéo sur Channel9 avec Brian Beckman et Erik Meijer où ils ont mentionné comment la mise en œuvre est parfois" tombe "de la signature de type d'une fonction. J'ai également vécu cela dans le passé, mais je ne peux pas proposer un bon exemple qui serait suffisamment simple à présenter à une personne sans expérience fonctionnelle antérieure. P>
Quelqu'un a-t-il un bon exemple à partager? (il n'est pas nécessaire d'être en f #) p>
Si c'est une aide, je pense que nous devons y penser différemment: le casse-tête est le suivant: P>
J'ai des données avec un type donné, je veux le transformer en un type différent et J'ai un ensemble de fonctions strong> avec des signatures données. p>
Ceci est le "LEGO" que vous devez brancher. P>
9 Réponses :
OK, ce n'est pas un excellent exemple, mais "OK", et peut-être aidera à obtenir d'autres idées ... p>
Supposons P>
let f() = Unchecked.defaultof<'a>
En fait, dans Haskell, vous pouvez jeter des exceptions i> à partir du code purs, mais ne les attrapez que dans le io code> monad. Donc
f () = erreur "kaboom!" Code> aurait le type
() -> A code>. (Voir "une sémantique pour des exceptions imprécises" pour les détails sous-jacents de la raison pour laquelle cela va bien.)
Supposons que je souhaite écrire une fonction f p> dans F #, je pense que la seule implémentation intéressante est la suivante: p> La fonction d'identité ci-dessus est la mise en œuvre naturelle de la plupart des langues. p> et comme dans mon autre réponse, vous pouvez également boucler, lancer ou par défaut à initialiser. Mais ce sont moins intéressants. Ces "backdoors" jettent un peu une clé dans les œuvres de tous ces calculs "de type dérivés", il est donc préférable de les ignorer. P> La clé de celui-ci est, pour tous les types "A, vous Ne rien savoir sur le type, le seul moyen d'obtenir un véritable objet de ce type est que quelqu'un vous en donne déjà un. La fonction d'identité est donc la seule mise en œuvre raisonnable de cette signature. P> h2>
Voici un troisième exemple ... p>
Supposons que je veux écrire une fonction p> c'est-à-dire que je prends des arguments une valeur de type 'A et une fonction qui prend un "A et retourne a" b. Et mon résultat devrait être un 'b. Eh bien, encore une fois, des boucles et des exceptions infinies modulo et d'initialisation par défaut, il n'y a qu'une seule implication: p> "P" peut ne pas ressembler trop utile tant que vous ne réalisiez que c'est l'opérateur de pipeline ( |>). P> hm, je me sens comme ces exemples jusqu'à présent sont insuffisants. P> h2>
Commencez par la fonction la plus simple possible: Identity :: 'A ->' A code>. Combien d'implémentations pouvez-vous penser? Si vous me donnez un
A code>, il n'y a qu'une chose que je puisse faire avec cela pour vous donner un
A code>. Je vous rends le même
A code> que vous m'avez donné, alors:
let compose f g x = f (g x)
Peut-être que je manque quelque chose (s'il vous plaît éclairer moi), mais ne serait-il pas carré avoir la même signature que l'identité? Laissez SQ X = x * x
@Markus, c'est ce que je pensais aussi, mais (je pense) alors ce ne serait pas "A ->" A, mais INT -> int (ou quelque chose d'autre qui supporte * l'opérateur)
L'un des avantages des signatures de type est qu'ils vous disent tout autant de ce que vous ne pouvez pas faire avec une valeur comme ce que vous pouvez. Vous ne pouvez pas sécuriser un 'A code> (ou faire une grande partie de rien) car la fonction n'est pas définie pour tous les types. Ces limites font partie de ce qui le rend si facile de traduire une signature de type en code - il existe un nombre limité de choses que vous pouvez faire avec des variables de type générique et une liste encore plus limitée de choses qui ont du sens à faire.
Un couple de plus à envisager ... p> La seule implémentation intéressante est l'option.map: Maintenant, j'aurais pu écrire P> et ignorer simplement les arguments et revenir toujours aucun. Mais ce n'est pas intéressant. Quelqu'un nous passe tous ces arguments utiles, nous sommes sûrement censés faire quelque chose avec eux, non? La première mise en œuvre ci-dessus est donc la seule (à nouveau modulo les trivialités mentionnées dans d'autres réponses, en raison de la boucle, des effets, etc.). P> De même p> ('a -> 'b) -> 'a list -> 'b list
let lm f xs =
match xs with
| [] -> []
| h::t -> [f h]
Le type réel de let om f xo = Aucun code> est
'A ->' B -> 'C Option code>, il ne s'agit pas seulement d'uninterest, mais du mauvais type. Type Annotations Nonobstant.
(a -> b) -> [a] -> [b] The type practically is the implementation.
Exemple de démarreur idiot, suivant de ma mise à jour:
Donnez que j'ai une liste
tableau
//Start at the beginning
let input:List<string> = myData
// the only thing that I can apply to this is a
// function that starts with List<something>, so...
input |> fn2 // List<'a> -> Array<'b>, so now I have Array<string>
// At this point, it looks like I have no choice but fn3, but that takes
// me back where I came from. However, there is an Array<'a> in
// the middle of fn4.
input |> fn2 |> fn4 ???
//oops, I need something else here, a function that goes ('a -> 'b).
// But all my functions go ('a -> 'b)! However, in this case my 'a is a string,
// so that limits my choice to fn1:
input |> fn2 |> fn4 fn1 // so here I have Array<float> yoohoo!
//recapitulate with the real function names
let convert input =
input |> Array.ofList //List<string> -> Array<string>
|> Array.map float //Array<string> -> (string -> float) -> Array<float>
Une autre réponse provient de Wes Dyer , qui mentionne ce phénomène précis lors de l'explication de Select pour iobservable. La signature étant:
IObservable<U> Select<T, U>(this IObservable<T> source, Func<T, U> selector)
voir
Brièvement, l'objectif est de résoudre pour f CODE> ICI: P>
> [Some 4; None; Some 2; None] |> f;;
val it : int list = [4; 2]
Je n'ai pas d'exemple simple de cela (mais je continuerai à la nouilles dessus ...) Je peux penser à un certain nombre d'exemples compliqués ... Hmmm ...
Brian: Je serais intéressé à voir même un exemple compliqué! Il pourrait également servir de point de départ pour la communauté SO / F # pour distiller un exemple plus simple si votre «noodling» vient à rien ...