64 votes

Haskell type vs newtype à l'égard de la sécurité de type

Je connais newtype est le plus souvent comparé à d' data en Haskell, mais je suis en posant cette comparaison à partir de plus d'une conception de point-de-vue que comme un problème technique.

Dans imperitive/OO langues, il est l'anti-modèle "primitive obsession", où l'utilisation prolifique de types primitifs réduit la sécurité du type de programme et introduit accidentellement l'interchangeabilité de même des valeurs typées, sinon destinés à des fins différentes. Par exemple, beaucoup de choses peuvent être une Chaîne de caractères, mais ce serait bien si un compilateur pouvait savoir, de manière statique, ce qui signifie pour nous être d'un nom et d'où nous l'entendons à la ville, dans une adresse.

Oui, à quelle fréquence puis, ne Haskell programmeurs emploient newtype pour donner le type de distinctions pour toute autre manière de valeurs primitives? L'utilisation de l' type introduit un alias et donne un programme de lisibilité de la sémantique, mais n'empêche pas accidentellement échanges de valeurs. Comme je l'apprendre haskell, je remarque que le type de système est plus puissant que tout ce que j'ai trouver. Donc, je pense que c'est un naturel et d'une pratique courante, mais je n'ai pas vu beaucoup de discuter de l'utilisation de newtype dans cette lumière.

Bien sûr, beaucoup de programmeurs de faire les choses différemment, mais est-ce à toutes les communes en haskell?

59voto

Christopher Done Points 2699

Les principaux usages des newtypes sont:

  1. Pour définir différentes instances de types.
  2. De la Documentation.
  3. Données/format de l'exactitude de l'assurance.

Je suis en train de travailler sur une application dès maintenant dans lequel j'utilise newtypes largement. newtypes en Haskell sont purement moment de la compilation concept. E. g. avec unwrappers ci-dessous, unFilename (Filename "x") compilés dans le même code "x". Il n'y a absolument zéro au moment de l'exécution de frapper. Il est avec data types. Cela en fait un bon moyen d'atteindre les objectifs énumérés ci-dessus.

-- | A file name (not a file path).
newtype Filename = Filename { unFilename :: String }
    deriving (Show,Eq)

Je ne veux pas accidentellement traiter cela comme un chemin de fichier. Ce n'est pas un chemin d'accès au fichier. C'est le nom d'un modèle conceptuel de fichier quelque part dans la base de données.

Il est très important pour les algorithmes à se référer au droit chose, newtypes aider avec ceci. Il est aussi très important pour la sécurité, par exemple, envisager d'envoyer des fichiers d'une application web. J'ai ces types:

-- | A sanitized (safe) filename.
newtype SanitizedFilename = 
  SanitizedFilename { unSafe :: String } deriving Show

-- | Unique, sanitized filename.
newtype UniqueFilename =
  UniqueFilename { unUnique :: SanitizedFilename } deriving Show

-- | An uploaded file.
data File = File {
   file_name     :: String         -- ^ Uploaded file.
  ,file_location :: UniqueFilename -- ^ Saved location.
  ,file_type     :: String         -- ^ File type.
  } deriving (Show)

Supposons que j'ai cette fonction qui nettoie un nom de fichier à partir d'un fichier qui a été téléchargé:

-- | Sanitize a filename for saving to upload directory.
sanitizeFilename :: String            -- ^ Arbitrary filename.
                 -> SanitizedFilename -- ^ Sanitized filename.
sanitizeFilename = SanitizedFilename . filter ok where 
  ok c = isDigit c || isLetter c || elem c "-_."

Maintenant depuis que je génère un nom de fichier unique:

-- | Generate a unique filename.
uniqueFilename :: SanitizedFilename -- ^ Sanitized filename.
               -> IO UniqueFilename -- ^ Unique filename.

C'est dangereux pour générer un nom de fichier unique à partir d'un nom de fichier arbitraire, il doit être désinfecté en premier. De même, un nom de fichier unique est donc toujours en sécurité par extension. Je peux enregistrer le fichier sur le disque maintenant et de mettre le nom du fichier dans ma base de données si je veux.

Mais il peut aussi être gênant d'avoir à enrouler/dérouler beaucoup. Dans le long terme, je vois qu'il vaut en particulier pour éviter de valeur différences. ViewPatterns aider un peu:

-- | Get the form fields for a form.
formFields :: ConferenceId -> Controller [Field]
formFields (unConferenceId -> cid) = getFields where
   ... code using cid ..

Peut-être que vous allez dire que déballer dans une fonction est un problème: que faire si vous passez cid pour une fonction à tort? Pas de problème, toutes les fonctions à l'aide d'un id de la conférence sera l'utilisation de la ConferenceId type. Ce qui émerge est une sorte de fonction-fonction de niveau système de contrat qui est forcé au moment de la compilation. Assez agréable. Donc oui je l'utilise aussi souvent que je peux, en particulier dans les grands systèmes.

19voto

Paul Johnson Points 8604

Je pense que c'est surtout une question de la situation.

Envisager des chemins d'accès. La norme prélude a "type FilePath = Chaîne de caractères" parce que, comme une question de commodité, vous souhaitez avoir accès à l'ensemble de la chaîne de caractères et de la liste des opérations. Si vous avez eu "newtype FilePath = String FilePath" alors vous devez filePathLength, filePathMap et ainsi de suite, ou autre chose qui vous seraient toujours à l'aide de fonctions de conversion.

D'autre part, examiner les requêtes SQL. L'injection SQL est une commune de trou de sécurité, il est donc logique d'avoir quelque chose comme

newtype Query = Query String

puis ajouter des fonctions supplémentaires qui vous permet de convertir une chaîne de caractères dans une requête (ou de la requête fragment) par échapper les guillemets, ou de remplir les blancs dans un modèle de la même manière. De cette façon, vous ne pouvez pas accidentellement convertir un utilisateur paramètre dans une requête sans passer par la citation échapper la fonction.

17voto

Curt Sampson Points 10866

Pour la simple X = Y des déclarations, type documentation; newtype est le type de la vérification; c'est pourquoi, newtype est comparé à d' data.

J'ai assez souvent d'utiliser newtype pour le seul but de vous décrire: veiller à ce que quelque chose qui est stocké (et souvent manipulé) de la même façon comme une autre de type n'est pas confondu avec autre chose. De cette façon, il fonctionne exactement comme un peu plus efficace, data déclaration; il n'y a pas de raison particulière d'en choisir un sur l'autre. Notez que GHC l' GeneralizedNewtypeDeriving extension, soit vous pouvez dériver automatiquement des classes d' Num, permettant à vos températures ou yen à ajouter, et soustrait comme vous pouvez le faire avec l' Ints ou ce qui se trouve en dessous. On veut être un peu prudent avec cela, cependant; en général, on n'a pas multiplier une température par une autre température!

Pour avoir une idée de la façon dont souvent ces choses sont utilisés, Dans un assez grand projet sur lequel je travaille en ce moment, j'ai environ 122 utilise des data, 39 utilisations de l' newtypeet 96 utilise des type.

Mais le rapport, aussi loin que la "simple" types sont concernés, c'est un peu plus étroite que le démontre, car 32 96 utilise des type sont en fait des alias pour les types de fonctions, telles que

type PlotDataGen t = PlotSeries t -> [String]

Vous remarquerez deux complexités ici: d'abord, c'est en fait un type de fonction, et pas seulement un simple X = Y alias, et, deuxièmement, que c'est paramétrable: PlotDataGen est un constructeur de type que j'applique à un autre type pour créer un nouveau type, comme PlotDataGen (Int,Double). Quand vous commencez à faire ce genre de chose, type n'est plus seulement de la documentation, mais est en fait une fonction, mais au niveau des types plutôt que le niveau de données.

newtype est parfois utilisée lorsqu' type ne peut pas être, comme lorsque récursive de la définition de type est nécessaire, mais je trouve que cela soit assez rare. Donc, il semble que, sur ce projet en particulier au moins, environ 40% de mon "primitive" type de définitions newtypes et 60% sont types. Plusieurs de l' newtype définitions utilisés pour être, et ils étaient certainement converti pour connaître précisément les raisons que vous avez mentionnées.

Donc, en bref, oui, c'est souvent un idiome.

10voto

Ganesh Sittampalam Points 17695

Je pense que c'est assez courant d'utiliser newtype pour le type distinctions. Dans de nombreux cas, c'est parce que vous voulez donner différents type des instances de classe, ou pour cacher les implémentations, mais simplement vouloir se protéger contre les chocs, les conversions est aussi une raison évidente à faire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X